xserver-xorg-video-qxl-0.1.1/0000775000000000000000000000000012233752255012770 5ustar xserver-xorg-video-qxl-0.1.1/README0000664000000000000000000000136712233750620013651 0ustar The QXL virtual GPU is found in the RedHat Enterprise Virtualisation system, and also in the spice project. All questions regarding this software should be directed at the Xorg mailing list: http://lists.freedesktop.org/mailman/listinfo/xorg Please submit bug reports to the Xorg bugzilla: https://bugs.freedesktop.org/enter_bug.cgi?product=xorg The master development code repository can be found at: git://anongit.freedesktop.org/git/xorg/driver/xf86-video-qxl http://cgit.freedesktop.org/xorg/driver/xf86-video-qxl For patch submission instructions, see: http://www.x.org/wiki/Development/Documentation/SubmittingPatches For more information on the git code manager, see: http://wiki.x.org/wiki/GitPage xserver-xorg-video-qxl-0.1.1/TODO.xspice0000664000000000000000000000132712046675641014762 0ustar TODO: 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.1/ign0000664000000000000000000000176612233752231013474 0ustar # # 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' # !debian/Xspice.1 xserver-xorg-video-qxl-0.1.1/NEWS0000664000000000000000000000065312233751142013465 0ustar 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.1/src/0000775000000000000000000000000012233752225013554 5ustar xserver-xorg-video-qxl-0.1.1/src/qxl_drmmode.h0000664000000000000000000000543612233751142016245 0ustar /* * 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; } 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.1/src/mspace.h0000664000000000000000000001130212233750620015167 0ustar #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.1/src/spiceqxl_io_port.h0000664000000000000000000000252612233750620017312 0ustar /* * 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.1/src/spiceqxl_uinput.h0000664000000000000000000000017512233751142017161 0ustar #ifndef SPICEQXL_UINPUT_H #define SPICEQXL_UINPUT_H #include "qxl.h" void spiceqxl_uinput_init(qxl_screen_t *qxl); #endif xserver-xorg-video-qxl-0.1.1/src/spiceqxl_uinput.c0000664000000000000000000000660012233751142017153 0ustar #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include "qxl_option_helpers.h" #include "spiceqxl_inputs.h" #include "spiceqxl_uinput.h" static const char *uinput_filename; static int uinput_fd; 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; } 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; } qxl->core->watch_add(uinput_fd, SPICE_WATCH_EVENT_READ, spiceqxl_uinput_read_cb, qxl); spice_server_set_agent_mouse(qxl->spice_server, 1); } xserver-xorg-video-qxl-0.1.1/src/uxa/0000775000000000000000000000000012233752225014351 5ustar xserver-xorg-video-qxl-0.1.1/src/uxa/uxa-priv.h0000664000000000000000000002646012233751517016310 0ustar /* * * 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.1/src/uxa/uxa.h0000664000000000000000000005264112233750620015324 0ustar /* * 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 * can not 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.1/src/uxa/uxa-damage.c0000664000000000000000000006501612233750620016533 0ustar /* * 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 "scrnintstr.h" #include "windowstr.h" #include "dixfontstr.h" #include "gcstruct.h" #include "picturestr.h" #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; QueryGlyphExtents(font, charinfo, n, &extents); 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.1/src/uxa/uxa-unaccel.c0000664000000000000000000003406012233750620016722 0ustar /* * * 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.1/src/uxa/uxa-damage.h0000664000000000000000000001153512233750620016535 0ustar 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); 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.1/src/uxa/Makefile.am0000664000000000000000000000060212233750620016400 0ustar noinst_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.1/src/uxa/uxa.c0000664000000000000000000004231012233750620015307 0ustar /* * 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.1/src/uxa/uxa-render.c0000664000000000000000000014076012233751142016574 0ustar /* * 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, yDst_copy; 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, yDst_copy; 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.1/src/uxa/uxa-accel.c0000664000000000000000000010234612233751142016362 0ustar /* * 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.1/src/uxa/uxa-glyphs.c0000664000000000000000000007323612233751142016626 0ustar /* * 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, height, 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.1/src/qxl_surface.h0000664000000000000000000000215112233751142016235 0ustar #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.1/src/qxl_surface.c0000664000000000000000000005423012233751142016235 0ustar /* * 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; if (!qxl->kms_enabled) drawable->mm_time = qxl->rom->mm_clock; 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) { 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; rect.left = b->x1; rect.right = b->x2; rect.top = b->y1; rect.bottom = b->y2; 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, b->x1, b->y1, b->x2 - b->x1, b->y2 - b->y1, 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.1/src/qxl_kms.c0000664000000000000000000004362312233751142015403 0ustar #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 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 = 1024; qxl->virtual_y = 768; 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; 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; 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); 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; pScrn->virtualX = pScrn->currentMode->HDisplay; pScrn->virtualY = pScrn->currentMode->VDisplay; 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++; } 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.1/src/compat-api.h0000664000000000000000000000674312233750620015766 0ustar /* * 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.1/src/Makefile.am0000664000000000000000000000660112233751142015610 0ustar # 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 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_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_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 \ qxl_uxa.c \ qxl_ums_mode.c \ qxl_io.c \ compat-api.h endif xserver-xorg-video-qxl-0.1.1/src/qxl_image.c0000664000000000000000000001622512233751142015671 0ustar /* * 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" typedef struct image_info_t image_info_t; struct image_info_t { struct QXLImage *image; int ref_count; image_info_t *next; }; #define HASH_SIZE 4096 static image_info_t *image_table[HASH_SIZE]; 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 (dest) memcpy (dest_line, src_line, n_bytes); MurmurHash3_x86_32 (src_line, n_bytes, hash, &hash); } return hash; } static image_info_t * lookup_image_info (unsigned int hash, int width, int height) { struct image_info_t *info = image_table[hash % HASH_SIZE]; while (info) { struct QXLImage *image = info->image; if (image->descriptor.id == hash && image->descriptor.width == width && image->descriptor.height == height) { return info; } info = info->next; } #if 0 ErrorF ("lookup of %u failed\n", hash); #endif return NULL; } static image_info_t * insert_image_info (unsigned int hash) { struct image_info_t *info = malloc (sizeof (image_info_t)); if (!info) return NULL; info->next = image_table[hash % HASH_SIZE]; image_table[hash % HASH_SIZE] = info; return info; } static void remove_image_info (image_info_t *info) { struct image_info_t **location = &image_table[info->image->descriptor.id % HASH_SIZE]; while (*location && (*location) != info) location = &((*location)->next); if (*location) *location = info->next; free (info); } 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; image_info_t *info; struct QXLImage *image; struct qxl_bo *head_bo, *tail_bo; struct qxl_bo *image_bo; int dest_stride = (width * Bpp + 3) & (~3); int h; 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; while (h) { int chunk_size = MAX (512 * 512, dest_stride); 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)) { if ((info = insert_image_info (hash))) { info->image = image; info->ref_count = 1; 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; image_info_t *info; uint64_t chunk, prev_chunk; image = qxl->bo_funcs->bo_map(image_bo); info = lookup_image_info (image->descriptor.id, image->descriptor.width, image->descriptor.height); qxl->bo_funcs->bo_unmap(image_bo); if (info && info->image == image) { --info->ref_count; if (info->ref_count != 0) return; #if 0 ErrorF ("removed %p from hash table\n", info->image); #endif remove_image_info (info); } 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); } void qxl_drop_image_cache (qxl_screen_t *qxl) { memset (image_table, 0, HASH_SIZE * sizeof (image_info_t *)); } xserver-xorg-video-qxl-0.1.1/src/spiceqxl_inputs.c0000664000000000000000000003162512233751142017156 0ustar /* * 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; int i; kbd = device->public.devicePrivate; 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.1/src/qxl_io.c0000664000000000000000000001130512233751142015210 0ustar /* * 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)) usleep (1); 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.1/src/spiceqxl_io_port.c0000664000000000000000000002244512233751142017307 0ustar /* * 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 dupplication */ #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; \ } 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.1/src/spiceqxl_main_loop.h0000664000000000000000000000253012233751142017607 0ustar /* * 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.1/src/qxl_ring.c0000664000000000000000000001002712233751142015540 0ustar /* * 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.1/src/murmurhash3.h0000664000000000000000000000217512233750620016205 0ustar // 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.1/src/qxl.h0000664000000000000000000004540312233751142014534 0ustar /* * 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 #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 #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, #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; 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 void * io_pages; void * io_pages_physical; #ifdef XSERVER_LIBPCIACCESS struct pci_device * pci; #else pciVideoPtr pci; PCITAG pci_tag; #endif 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; pthread_t audio_thread; 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]; #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); void qxl_drop_image_cache (qxl_screen_t *qxl); /* * 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); void * qxl_allocnf (qxl_screen_t *qxl, unsigned long size, const char * name); 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) { outb(qxl->io_base + 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.1/src/qxl_drmmode.c0000664000000000000000000006316112233751142016237 0ustar /* * 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" /* DPMS */ #ifdef HAVE_XEXTPROTO_71 #include #else #define DPMS_SERVER #include #endif #include #include "qxl.h" #include "qxl_surface.h" 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; 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 (!xf86CrtcRotate(crtc)) { goto done; } #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)); 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 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 == -ENOSYS) 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; 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].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); name = NameForAtom(atom); /* 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) { return TRUE; } 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", "DVI", "DVI", "Composite", "S-video", "LVDS", "CTV", "DIN", "DisplayPort", "HDMI", "HDMI", "TV", "eDP", "Virtual" }; 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; } } /* need to do smart conversion here for compat with non-kms ATI driver */ snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id - 1); 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; drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation, crtc->x, crtc->y); } { 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); 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.1/src/spiceqxl_display.c0000664000000000000000000003005312233751142017273 0ustar /* * 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) */ 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: reenable, 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, (QXLPHYSICAL)qxl->monitors_config, MEMSLOT_GROUP, 0); } xserver-xorg-video-qxl-0.1.1/src/spiceqxl_spice_server.h0000664000000000000000000000260412233750620020325 0ustar /* * 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.1/src/mspace.c0000664000000000000000000024332312233750620015174 0ustar // 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 plaftorms */ #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 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 an 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 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.1/src/qxl_driver.c0000664000000000000000000011766512233751142016114 0ustar /* * 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_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; 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}, /* VVV qemu defaults to 1 - not implemented in xspice yet */ { OPTION_SPICE_AGENT_MOUSE, "SpiceAgentMouse", OPTV_BOOLEAN, {0}, FALSE}, { 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}, #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 (RAM_SIZE, 1); qxl->ram_size = RAM_SIZE; qxl->ram_physical = qxl->ram; qxl->vram = calloc (VRAM_SIZE, 1); qxl->vram_size = VRAM_SIZE; 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); #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_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); qxl_drop_image_cache (qxl); 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_drop_image_cache (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 () - qxl->rom->surface0_area_size) / 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) { ScreenPtr pScreen; 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; pScreen = qxl->pScrn->pScreen; if (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); 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 (); spice_server_init (qxl->spice_server, qxl->core); qxl_add_spice_display_interface (qxl); qxl_add_spice_playback_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->uxa = uxa_driver_alloc (); #ifndef XSPICE qxl->io_pages = (void *)((unsigned long)qxl->ram); qxl->io_pages_physical = (void *)((unsigned long)qxl->ram_physical); #endif 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); qxl_drop_image_cache (qxl); } 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; #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'; #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; } #ifdef XF86DRM_MODE 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; } static Bool qxl_driver_func (ScrnInfoPtr screen_info_ptr, xorgDriverFuncOp xorg_driver_func_op, pointer hw_flags) { *(xorgHWFlags*)hw_flags = (xorgHWFlags)HW_SKIP_CONSOLE; 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 #endif /* XSPICE */ static DriverRec qxl_driver = { 0, driver_name, qxl_identify, qxl_probe, qxl_available_options, NULL, 0, #ifdef XSPICE qxl_driver_func, NULL, NULL #else NULL, #ifdef XSERVER_LIBPCIACCESS qxl_device_match, qxl_pci_probe #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.1/src/qxl_option_helpers.h0000664000000000000000000000070212233751142017637 0ustar #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.1/src/spiceqxl_driver.h0000664000000000000000000000265212233750620017132 0ustar /* * 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 VGA_RAM_SIZE (16 * 1024 * 1024) #define RAM_SIZE (128L<<20) // must be >VGA_RAM_SIZE #define VRAM_SIZE (128L<<20) #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.1/src/qxl_ums_mode.c0000664000000000000000000003147112233751142016417 0ustar /* * 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].x_res * qxl->modes[i].y_res * 4; if (size > qxl->surface0_size) { ErrorF ("skipping mode %dx%d not fitting in surface0", 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.1/src/qxl_cursor.c0000664000000000000000000001345412233751142016125 0ustar /* * 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); } 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.1/src/qxl_option_helpers.c0000664000000000000000000000204712233750620017636 0ustar #ifdef HAVE_CONFIG_H #include "config.h" #endif #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) { if (getenv(env_name)) { /* we don't support the whole range of boolean true and * false values documented in man xorg.conf, just the c * convention - 0 is false, anything else is true, so * just like a number. */ return !!atoi(getenv(env_name)); } return options[option_index].value.bool; } xserver-xorg-video-qxl-0.1.1/src/spiceqxl_display.h0000664000000000000000000000271212233751142017301 0ustar /* * 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.1/src/spiceqxl_main_loop.c0000664000000000000000000002302412233751142017603 0ustar /* * 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->xorg_timer = TimerSet(NULL, 0, 1e9 /* TODO: infinity? */, xorg_timer_callback, timer); timer->func = func; timer->opaque = opaque; return timer; } static void timer_start(SpiceTimer *timer, uint32_t ms) { 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); } 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 = malloc(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 void channel_event(int event, SpiceChannelEventInfo *info) { NOT_IMPLEMENTED } 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 calles wakeup_handler with the read fd_set, so we * must either patch it or do a polling select ourselves, this is the * later 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) { 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(); } SpiceCoreInterface *basic_event_loop_init(void) { ring_init(&watches); bzero(&core, sizeof(core)); core.base.major_version = SPICE_INTERFACE_CORE_MAJOR; core.base.minor_version = SPICE_INTERFACE_CORE_MINOR; // anything less then 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) { RegisterBlockAndWakeupHandlers(xspice_block_handler, xspice_wakeup_handler, 0); } xserver-xorg-video-qxl-0.1.1/src/spiceqxl_audio.c0000664000000000000000000002315712233751142016736 0ustar /* * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "spiceqxl_audio.h" #include #include #include #include #include #include #define BUFFER_PERIODS 10 #define PERIOD_MS 10 #define MAX_FIFOS 16 struct audio_data { int fifo_fds[MAX_FIFOS]; ino_t inodes[MAX_FIFOS]; uint32_t valid_bytes, write_offs; char *buffer, *spice_buffer; int period_frames; uint32_t spice_write_offs, spice_buffer_bytes; uint32_t frame_bytes, period_bytes, fed, buffer_bytes; struct timeval last_read_time; }; static ssize_t read_from_fifos (struct audio_data *data) { size_t to_read_bytes = min(data->period_bytes, data->buffer_bytes - data->write_offs); int i; ssize_t max_readed = 0; int16_t *out_buf = (int16_t*)(data->buffer + data->write_offs), *buf; buf = malloc(to_read_bytes); if (!buf) { ErrorF("playback: malloc failed: %s\n", strerror(errno)); return 0; } memset(out_buf, 0, to_read_bytes); for (i = 0; i < MAX_FIFOS; ++i) { unsigned int s; ssize_t readed; if (data->fifo_fds[i] < 0) continue; readed = read(data->fifo_fds[i], buf, to_read_bytes); if (readed < 0) { if (errno != EAGAIN && errno != EINTR) ErrorF("playback: read from FIFO %d failed: %s\n", data->fifo_fds[i], strerror(errno)); continue; } if (readed == 0) { ErrorF("playback: FIFO %d gave EOF\n", data->fifo_fds[i]); close(data->fifo_fds[i]); data->fifo_fds[i] = -1; data->inodes[i] = 0; continue; } if (readed > max_readed) max_readed = readed; for (s = 0; s < readed / 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_buf[s] + buf[s] > INT16_MAX) out_buf[s] = INT16_MAX; else if (out_buf[s] + buf[s] < -INT16_MAX) out_buf[s] = -INT16_MAX; else out_buf[s] += buf[s]; } } free(buf); if (!max_readed) return 0; data->valid_bytes = min(data->valid_bytes + max_readed, data->buffer_bytes); data->write_offs += max_readed; data->write_offs %= data->buffer_bytes; ++data->fed; return max_readed; } static int scan_fifos (struct audio_data *data, const char *dirname) { DIR *dir; struct dirent *ent; int i; dir = opendir(dirname); if (!dir) { ErrorF("playback: failed to open FIFO directory '%s': %s\n", dirname, strerror(errno)); return 1; } while ((ent = readdir(dir))) { char path[PATH_MAX]; if (ent->d_name[0] == '.') /* skip dot-files */ continue; for (i = 0; i < MAX_FIFOS; ++i) if (ent->d_ino == data->inodes[i]) break; if (i < MAX_FIFOS) /* file already open */ continue; for (i = 0; i < MAX_FIFOS; ++i) if (data->fifo_fds[i] < 0) break; if (i == MAX_FIFOS) { static int once = 0; if (!once) { ErrorF("playback: Too many FIFOs already open\n"); ++once; } closedir(dir); return 0; } if (snprintf(path, sizeof(path), "%s/%s", dirname, ent->d_name) >= sizeof(path)) { ErrorF("playback: FIFO filename is too long - truncated into %s", path); } data->fifo_fds[i] = open(path, O_RDONLY | O_RSYNC | O_NONBLOCK); if (data->fifo_fds[i] < 0) { ErrorF("playback: open FIFO '%s' failed: %s\n", path, strerror(errno)); continue; } ErrorF("playback: opened FIFO '%s' as %d\n", path, data->fifo_fds[i]); data->inodes[i] = ent->d_ino; } closedir(dir); return 0; } static void * audio_thread_main (void *p) { qxl_screen_t *qxl = p; int i; struct audio_data data; for (i = 0; i < MAX_FIFOS; ++i) data.fifo_fds[i] = -1; data.valid_bytes = data.fed = 0; data.period_frames = SPICE_INTERFACE_PLAYBACK_FREQ * PERIOD_MS / 1000; data.frame_bytes = sizeof(int16_t) * SPICE_INTERFACE_PLAYBACK_CHAN; data.period_bytes = data.period_frames * data.frame_bytes; data.buffer_bytes = data.period_bytes * BUFFER_PERIODS; data.buffer = malloc(data.buffer_bytes); memset(data.buffer, 0, data.buffer_bytes); spice_server_playback_start(&qxl->playback_sin); gettimeofday(&data.last_read_time, NULL); while (1) { struct timeval end, diff, period_tv; if (scan_fifos(&data, qxl->playback_fifo_dir)) goto cleanup; while (data.fed < BUFFER_PERIODS) { if (!read_from_fifos(&data)) break; while (data.valid_bytes) { int to_copy_bytes; uint32_t read_offs; if (!data.spice_buffer) { uint32_t chunk_frames; spice_server_playback_get_buffer(&qxl->playback_sin, (uint32_t**)&data.spice_buffer, &chunk_frames); data.spice_buffer_bytes = chunk_frames * data.frame_bytes; } if (!data.spice_buffer) break; if (data.valid_bytes > data.write_offs) { read_offs = data.buffer_bytes + data.write_offs - data.valid_bytes; to_copy_bytes = min(data.buffer_bytes - read_offs, data.spice_buffer_bytes - data.spice_write_offs); } else { read_offs = data.write_offs - data.valid_bytes; to_copy_bytes = min(data.valid_bytes, data.spice_buffer_bytes - data.spice_write_offs); } memcpy(data.spice_buffer + data.spice_write_offs, data.buffer + read_offs, to_copy_bytes); data.valid_bytes -= to_copy_bytes; data.spice_write_offs += to_copy_bytes; if (data.spice_write_offs >= data.spice_buffer_bytes) { spice_server_playback_put_samples(&qxl->playback_sin, (uint32_t*)data.spice_buffer); data.spice_buffer = NULL; data.spice_buffer_bytes = data.spice_write_offs = 0; } } } period_tv.tv_sec = 0; period_tv.tv_usec = PERIOD_MS * 1000; usleep(period_tv.tv_usec); gettimeofday(&end, NULL); timersub(&end, &data.last_read_time, &diff); while (data.fed && (diff.tv_sec > 0 || diff.tv_usec >= period_tv.tv_usec)) { timersub(&diff, &period_tv, &diff); --data.fed; timeradd(&data.last_read_time, &period_tv, &data.last_read_time); } if (!data.fed) data.last_read_time = end; } cleanup: if (data.spice_buffer) { memset(data.spice_buffer, 0, data.spice_buffer_bytes - data.spice_write_offs); spice_server_playback_put_samples(&qxl->playback_sin, (uint32_t*)data.spice_buffer); data.spice_buffer = NULL; data.spice_buffer_bytes = data.spice_write_offs = 0; } free(data.buffer); spice_server_playback_stop(&qxl->playback_sin); return NULL; } static const SpicePlaybackInterface playback_sif = { { SPICE_INTERFACE_PLAYBACK, "playback", SPICE_INTERFACE_PLAYBACK_MAJOR, SPICE_INTERFACE_PLAYBACK_MINOR } }; int qxl_add_spice_playback_interface (qxl_screen_t *qxl) { int ret; if (qxl->playback_fifo_dir[0] == 0) { ErrorF("playback: no audio FIFO directory, audio is disabled\n"); 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) return errno; /* disable CELT */ ret = spice_server_set_playback_compression(qxl->spice_server, 0); if (ret < 0) return errno; ret = pthread_create(&qxl->audio_thread, NULL, &audio_thread_main, qxl); if (ret < 0) return errno; return 0; } xserver-xorg-video-qxl-0.1.1/src/spiceqxl_vdagent.c0000664000000000000000000001150412233751142017256 0ustar #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "qxl_option_helpers.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; 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 read; if (virtio_client_fd == -1) { return 0; } read = recv(virtio_client_fd, buf, len, 0); if (read <= 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { return 0; } fprintf(stderr, "ERROR: vdagentd died\n"); close(virtio_client_fd); virtio_client_fd = -1; vdagent_sin.qxl->core->watch_remove(virtio_client_watch); virtio_client_watch = NULL; } return read; } 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); 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; } 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; spice_server_add_interface(qxl->spice_server, &vdagent_sin.base.base); spiceqxl_uinput_init(qxl); } xserver-xorg-video-qxl-0.1.1/src/spiceqxl_spice_server.c0000664000000000000000000002564712233751142020334 0ustar /* * 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 precedense. 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"); 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); spice_server_set_port(spice_server, 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 = strdup(x509_key_file_base); } else { x509_key_file = malloc(len); snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE); } if (x509_cert_file_base) { x509_cert_file = strdup(x509_cert_file_base); } else { x509_cert_file = malloc(len); snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE); } if (x509_cacert_file_base) { x509_cacert_file = strdup(x509_cert_file_base); } else { x509_cacert_file = malloc(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 (disable_ticketing) { spice_server_set_noauth(spice_server); } #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); } 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.1/src/qxl_uxa.c0000664000000000000000000003237112233751142015404 0ustar /* * 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) { static Bool result, checked; if (!checked) { result = qxl_kms_check_cap(qxl, SPICE_DISPLAY_CAP_COMPOSITE); checked = TRUE; } return result; } #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) { static Bool result, checked; if (!checked) { result = qxl_kms_check_cap(qxl, SPICE_DISPLAY_CAP_A8_SURFACE); checked = TRUE; } return result; } #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 (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.1/src/spiceqxl_vdagent.h0000664000000000000000000000020012233751142017252 0ustar #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.1/src/spiceqxl_driver.c0000664000000000000000000001276312233751142017131 0ustar /* * 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), #if VGA_RAM_SIZE >= (16 * 1024 * 1024) /* these modes need more than 8 MB video memory */ 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), #endif #if VGA_RAM_SIZE >= (32 * 1024 * 1024) /* these modes need more than 16 MB video memory */ QXL_MODE_EX(2560, 2048), QXL_MODE_EX(2800, 2100), QXL_MODE_EX(3200, 2400), #endif }; // 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 surface0_area_size; uint32_t num_pages; uint32_t fb, maxfb = 0; int i; 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); modes->n_modes = (SPICE_ARRAY_SIZE(qxl_modes)); for (i = 0; i < modes->n_modes; i++) { fb = qxl_modes[i].y_res * qxl_modes[i].stride; if (maxfb < fb) { maxfb = fb; } modes->modes[i].id = (i); modes->modes[i].x_res = (qxl_modes[i].x_res); modes->modes[i].y_res = (qxl_modes[i].y_res); modes->modes[i].bits = (qxl_modes[i].bits); modes->modes[i].stride = (qxl_modes[i].stride); modes->modes[i].x_mili = (qxl_modes[i].x_mili); modes->modes[i].y_mili = (qxl_modes[i].y_mili); modes->modes[i].orientation = (qxl_modes[i].orientation); } if (maxfb < VGA_RAM_SIZE) // TODO - id != 0? (in original code from qxl.c) maxfb = VGA_RAM_SIZE; ram_header_size = ALIGN(sizeof(struct QXLRam), 4096); surface0_area_size = ALIGN(maxfb, 4096); num_pages = VRAM_SIZE; num_pages -= ram_header_size; num_pages -= surface0_area_size; num_pages = num_pages / TARGET_PAGE_SIZE; rom->draw_area_offset = (0); rom->surface0_area_size = (surface0_area_size); rom->pages_offset = (surface0_area_size); rom->num_pages = (num_pages); rom->ram_header_offset = (VRAM_SIZE - ram_header_size); qxl->shadow_rom = *qxl->rom; // TODO - do we need this? } xserver-xorg-video-qxl-0.1.1/src/spiceqxl_audio.h0000664000000000000000000000243212233751142016734 0ustar /* * 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.1/src/spiceqxl_inputs.h0000664000000000000000000000257412233751142017164 0ustar /* * 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.1/src/dfps.c0000664000000000000000000002435712233751142014664 0ustar /* * 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 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); } 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 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) { struct pixman_box16 box; RegionPtr region; Bool throwaway_bool; 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 */ box.x1 = x_1; box.x2 = x_2; box.y1 = y_1; box.y2 = y_2; region = RegionCreate(&box, 1); RegionAppend(&info->updated_region, region); RegionValidate(&info->updated_region, &throwaway_bool); RegionUninit(region); 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) { struct pixman_box16 box; RegionPtr region; Bool throwaway_bool; 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 */ box.x1 = dest_x1; box.x2 = dest_x1 + width; box.y1 = dest_y1; box.y2 = dest_y1 + height; region = RegionCreate(&box, 1); RegionAppend(&info->updated_region, region); RegionValidate(&info->updated_region, &throwaway_bool); RegionUninit(region); } 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) { struct pixman_box16 box; RegionPtr region; Bool throwaway_bool; dfps_info_t *info; if (!(info = dfps_get_info (dest))) return FALSE; box.x1 = x; box.x2 = x + w; box.y1 = y; box.y2 = y + h; region = RegionCreate(&box, 1); RegionAppend(&info->updated_region, region); RegionValidate(&info->updated_region, &throwaway_bool); RegionUninit(region); /* We can avoid doing the put image ourselves, as the uxa driver will fall back and do it for us if we return false */ return FALSE; } 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; Bool throwaway_bool; if (!(info = dfps_get_info (pixmap))) return FALSE; RegionAppend(&info->updated_region, region); RegionValidate(&info->updated_region, &throwaway_bool); } 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 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_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.1/src/qxl_mem.c0000664000000000000000000004435612233751142015373 0ustar /* * 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); } 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 refernce 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; } 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.1/src/murmurhash3.c0000664000000000000000000001745412233751142016206 0ustar //----------------------------------------------------------------------------- // 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.1/src/dfps.h0000664000000000000000000000242612233751142014662 0ustar /* * 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_ticker(void *opaque); void dfps_set_uxa_functions(qxl_screen_t *qxl, ScreenPtr screen); xserver-xorg-video-qxl-0.1.1/src/qxl_surface_ums.c0000664000000000000000000004777112233751142017135 0ustar /* * 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->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.1/Makefile.am0000664000000000000000000000256012233750620015021 0ustar # 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 \ README.xspice DISTCHECK_CONFIGURE_FLAGS=--enable-xspice xserver-xorg-video-qxl-0.1.1/debian/0000775000000000000000000000000012274406557014220 5ustar xserver-xorg-video-qxl-0.1.1/debian/Xspice.10000664000000000000000000000340012233750620015516 0ustar .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.8. .TH XSPICE "1" "April 2012" "Xspice" "User Commands" .SH NAME Xspice \- X server and SPICE server .SH DESCRIPTION usage: Xspice [Xspice and Xorg options intermixed] .PP example usage: Xspice \fB\-\-port\fR 5900 \fB\-\-disable\-ticketing\fR :1.0 .SS "optional arguments:" .TP \fB\-h\fR, \fB\-\-help\fR show this help message and exit .HP \fB\-\-xorg\fR XORG .HP \fB\-\-config\fR CONFIG .TP \fB\-\-port\fR PORT standard spice port .TP \fB\-\-tls\-port\fR TLS_PORT spice tls port .TP \fB\-\-disable\-ticketing\fR do not require a client password .TP \fB\-\-sasl\fR enable sasl .TP \fB\-\-x509\-dir\fR X509_DIR x509 directory for tls .TP \fB\-\-cacert\-file\fR CACERT_FILE ca certificate file for tls .TP \fB\-\-x509\-cert\-file\fR X509_CERT_FILE server certificate file for tls .TP \fB\-\-x509\-key\-file\fR X509_KEY_FILE server key file for tls .TP \fB\-\-x509\-key\-password\fR X509_KEY_PASSWORD key file password for tls .HP \fB\-\-tls\-ciphers\fR TLS_CIPHERS .HP \fB\-\-dh\-file\fR DH_FILE .TP \fB\-\-password\fR PASSWORD set password required to connect to server .TP \fB\-\-image\-compression\fR {off,auto_glz,auto_lz,quic,glz,lz} auto_glz by default .TP \fB\-\-jpeg\-wan\-compression\fR {auto,never,always} auto by default .TP \fB\-\-zlib\-glz\-wan\-compression\fR {auto,never,always} auto by default .TP \fB\-\-streaming\-video\fR {off,all,filter} filter by default .HP \fB\-\-ipv4\-only\fR .HP \fB\-\-ipv6\-only\fR .PP Any options not parsed by Xspice get passed to Xorg as is. .SH SEE ALSO Spice related documents can be found at http://www.spice-space.org/documentation.html .PP This manual page was generated with help2man by Liang Guo , for the Debian project (and may be used by others). xserver-xorg-video-qxl-0.1.1/debian/README.source0000664000000000000000000000444612233750620016373 0ustar ------------------------------------------------------ Quick Guide To Patching This Package For The Impatient ------------------------------------------------------ 1. Make sure you have quilt installed 2. Unpack the package as usual with "dpkg-source -x" 3. Run the "patch" target in debian/rules 4. Create a new patch with "quilt new" (see quilt(1)) 5. Edit all the files you want to include in the patch with "quilt edit" (see quilt(1)). 6. Write the patch with "quilt refresh" (see quilt(1)) 7. Run the "clean" target in debian/rules Alternatively, instead of using quilt directly, you can drop the patch in to debian/patches and add the name of the patch to debian/patches/series. ------------------------------------ Guide To The X Strike Force Packages ------------------------------------ The X Strike Force team maintains X packages in git repositories on git.debian.org in the pkg-xorg subdirectory. Most upstream packages are actually maintained in git repositories as well, so they often just need to be pulled into git.debian.org in a "upstream-*" branch. Otherwise, the upstream sources are manually installed in the Debian git repository. The .orig.tar.gz upstream source file could be generated using this "upstream-*" branch in the Debian git repository but it is actually copied from upstream tarballs directly. Due to X.org being highly modular, packaging all X.org applications as their own independent packages would have created too many Debian packages. For this reason, some X.org applications have been grouped into larger packages: xutils, xutils-dev, x11-apps, x11-session-utils, x11-utils, x11-xfs-utils, x11-xkb-utils, x11-xserver-utils. Most packages, including the X.org server itself and all libraries and drivers are, however maintained independently. The Debian packaging is added by creating the "debian-*" git branch which contains the aforementioned "upstream-*" branch plus the debian/ repository files. When a patch has to be applied to the Debian package, two solutions are involved: * If the patch is available in one of the upstream branches, it may be git'cherry-picked into the Debian repository. In this case, it appears directly in the .diff.gz. * Otherwise, the patch is added to debian/patches/ which is managed with quilt as documented in /usr/share/doc/quilt/README.source. xserver-xorg-video-qxl-0.1.1/debian/rules0000775000000000000000000000161212233752023015263 0ustar #!/usr/bin/make -f DEB_HOST_GNU_TYPE := $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) .PHONY: override_dh_strip override_dh_strip: dh_strip --dbg-package=xserver-xorg-video-qxl-dbg # Install in debian/tmp to retain control through dh_install: override_dh_auto_install: dh_auto_install --destdir=debian/tmp # Kill *.la files, and forget no-one: override_dh_install: find debian/tmp -name '*.la' -delete -rm -r debian/tmp/usr/share dh_install --fail-missing # That's a plugin, use appropriate warning level: override_dh_shlibdeps: dh_shlibdeps -- --warnings=6 # Enable Xspice support override_dh_auto_configure: ifeq ($(DEB_HOST_GNU_TYPE), x86_64-linux-gnu) dh_auto_configure -- --enable-xspice else ifeq ($(DEB_HOST_GNU_TYPE), i486-linux-gnu) dh_auto_configure -- --enable-xspice else dh_auto_configure endif endif %: dh $@ --with quilt,autoreconf,xsf,python2 --builddirectory=build/ xserver-xorg-video-qxl-0.1.1/debian/copyright0000664000000000000000000000235012233750620016137 0ustar This package was downloaded from http://xorg.freedesktop.org/releases/individual/driver/ 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. src/murmurhash3.c, src/murmurhash3.h is public domain software, and written by Austin Appleby. xserver-xorg-video-qxl-0.1.1/debian/watch0000664000000000000000000000023612233750620015236 0ustar #git=git://anongit.freedesktop.org/xorg/driver/xf86-video-qxl version=3 http://xorg.freedesktop.org/releases/individual/driver/ xf86-video-qxl-(.*)\.tar\.bz2 xserver-xorg-video-qxl-0.1.1/debian/xserver-xspice.docs0000664000000000000000000000006212233750620020043 0ustar README.xspice examples/spiceqxl.xorg.conf.example xserver-xorg-video-qxl-0.1.1/debian/source/0000775000000000000000000000000012236474072015513 5ustar xserver-xorg-video-qxl-0.1.1/debian/source/format0000664000000000000000000000000412236474072016720 0ustar 1.0 xserver-xorg-video-qxl-0.1.1/debian/source/options0000664000000000000000000000023712233751142017124 0ustar diff-ignore = autogen.sh|.git|TODO.xspice|ChangeLog|Makefile.in|aclocal.m4|config.guess|config.h.in|config.sub|configure|depcomp|install-sh|ltmain.sh|missing xserver-xorg-video-qxl-0.1.1/debian/control0000664000000000000000000000507112236474072015621 0ustar Source: xserver-xorg-video-qxl Section: x11 Priority: optional Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Debian X Strike Force Uploaders: Liang Guo , Cyril Brulebois Build-Depends: debhelper (>= 8), pkg-config, dh-autoreconf, xserver-xorg-dev (>= 2:1.9.4), x11proto-core-dev, x11proto-fonts-dev, x11proto-randr-dev, x11proto-render-dev, x11proto-xext-dev, x11proto-xinerama-dev, x11proto-xf86dga-dev, dpkg-dev (>= 1.14.17), automake, libtool, xutils-dev (>= 1:7.5), quilt (>= 0.46-7~), libspice-protocol-dev (>= 0.12.2~), libspice-server-dev [amd64 i386], python, Standards-Version: 3.9.4 Homepage: http://spice-space.org/ Vcs-Git: git://git.debian.org/git/pkg-xorg/driver/xserver-xorg-video-qxl Vcs-Browser: http://git.debian.org/?p=pkg-xorg/driver/xserver-xorg-video-qxl.git Package: xserver-xorg-video-qxl Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, ${xviddriver:Depends} Provides: ${xviddriver:Provides} Description: X.Org X server -- QXL display driver This package provides the driver for QXL video device, i.e. if Linux is running inside a RedHat Enterprise Virtualization (RHEV) environment, or other SPICE-compatible KVM/Qemu emulator. . More information about X.Org can be found at: . This package is built from the X.org xf86-video-qxl driver module. Package: xserver-xorg-video-qxl-dbg Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, xserver-xorg-video-qxl (= ${binary:Version}) Section: debug Priority: extra Description: X.Org X server -- QXL display driver (debugging symbols) This package provides the driver for QXL video device, i.e. if Linux is running inside a RedHat Enterprise Virtualization (RHEV) environment, or other SPICE-compatible KVM/Qemu emulator. . This package contains the debugging symbols for this driver. Package: xserver-xspice Architecture: amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, xserver-xorg, xserver-xorg-video-qxl (>= 0.0.17-2), python-argparse Description: Xspice X server 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. xserver-xorg-video-qxl-0.1.1/debian/xserver-xorg-video-qxl.install0000664000000000000000000000004212233750620022151 0ustar usr/lib/xorg/modules/drivers/*.so xserver-xorg-video-qxl-0.1.1/debian/xserver-xorg-video-qxl.docs0000664000000000000000000000001412233750620021432 0ustar README TODO xserver-xorg-video-qxl-0.1.1/debian/xserver-xspice.manpages0000664000000000000000000000002012233750620020700 0ustar debian/Xspice.1 xserver-xorg-video-qxl-0.1.1/debian/xserver-xspice.install0000664000000000000000000000001012233750620020552 0ustar usr/bin xserver-xorg-video-qxl-0.1.1/debian/compat0000664000000000000000000000000212233750620015402 0ustar 7 xserver-xorg-video-qxl-0.1.1/debian/xserver-xorg-video-qxl.links0000664000000000000000000000012312233750620021623 0ustar usr/share/bug/xserver-xorg-core/script usr/share/bug/xserver-xorg-video-qxl/script xserver-xorg-video-qxl-0.1.1/debian/changelog0000664000000000000000000000715512274406515016074 0ustar xserver-xorg-video-qxl (0.1.1-0ubuntu3) trusty; urgency=medium * Rebuild for xorg 1.15. -- Maarten Lankhorst Wed, 05 Feb 2014 10:06:57 +0000 xserver-xorg-video-qxl (0.1.1-0ubuntu2) trusty; urgency=low * configure.ac: Add a check for XEXTPROTO_71. (FTBFS) -- Timo Aaltonen Wed, 06 Nov 2013 19:27:28 +0200 xserver-xorg-video-qxl (0.1.1-0ubuntu1) trusty; urgency=low * Sync from unreleased debian git. * Drop remove-mibstore.diff, upstream. * Enable xserver-spice again, the server is in main now. -- Timo Aaltonen Tue, 29 Oct 2013 17:26:46 +0200 xserver-xorg-video-qxl (0.1.1-1) UNRELEASED; urgency=low * New upstream release. -- Michele Cane Mon, 21 Oct 2013 22:07:36 +0200 xserver-xorg-video-qxl (0.1.0-2) unstable; urgency=low * Upload to unstable * Bump Standards-Version to 3.9.4 (no changes) * Enalbe Xspice in i386 (Closes: 689394) -- Liang Guo Fri, 17 May 2013 07:19:32 +0800 xserver-xorg-video-qxl (0.1.0-1) experimental; urgency=low [ Maarten Lankhorst ] * New upstream release. [ Timo Aaltonen ] * control: Bump build-dep on libspice-protocol-dev to >= 0.12.2~. [ Liang Guo ] * Ignore automatically generated files when build packages. -- Liang Guo Tue, 22 Jan 2013 23:36:17 +0800 xserver-xorg-video-qxl (0.0.17-2) unstable; urgency=low * Enable Xspice (Closes: 668537) * Add debian/xserver-xorg-video-qxl.docs. -- Liang Guo Mon, 16 Apr 2012 22:04:27 +0800 xserver-xorg-video-qxl (0.0.17-1) unstable; urgency=low [ Liang Guo ] * New upstream release. * debian/copyright: - Remove src/lookup3.c, removed upstream. - Add src/murmurhash3.{c,h}, new file. * debian/source/options: - Ignore ChangeLog, included in upstream tarbar, but not in git. * Remove translate-the-access-region.patch, applied upstream. * Bump Standards-Version to 3.9.3, no changes needed. [ Cyril Brulebois ] * Fix typo in the debug package's description. -- Liang Guo Fri, 16 Mar 2012 16:13:52 +0800 xserver-xorg-video-qxl (0.0.16-2) unstable; urgency=low [Serge Hallyn] * Add translate-the-access-region.patch(Closes: #655318) * Add debug package -- Liang Guo Wed, 01 Feb 2012 18:01:19 +0800 xserver-xorg-video-qxl (0.0.16-1) unstable; urgency=low [ Liang Guo ] * New upstream release * Temporarily ignore TODO.xspice -- Cyril Brulebois Sat, 12 Nov 2011 00:16:28 +0100 xserver-xorg-video-qxl (0.0.14-1) unstable; urgency=low * New upstream release: - Fix VT switching issues. * Add libspice-protocol-dev build-dep: - Versioned because of FDO#39249. * Bump Standards-Version to 3.9.2 (no changes). * Fix typo and missing space in long description. * Add myself to Uploaders. -- Cyril Brulebois Sat, 23 Jul 2011 12:15:54 +0200 xserver-xorg-video-qxl (0.0.13-1) unstable; urgency=low [ Liang Guo ] * New upstream version. * Remove xsfbs accordingly. * Switch to dh: - Use debhelper 8. - Use dh-autoreconf. * Remove fix_qxl_driver_assert.patch, already in upstream. * Bump Standards-Version to 3.9.1, no changes needed. * Add quilt build-dep. * Add HomePage control field. * Ignore autogen.sh changes when build source package. -- Julien Cristau Tue, 15 Feb 2011 18:27:12 +0100 xserver-xorg-video-qxl (0.0.12-1) unstable; urgency=low * Initial release (Closes: #576642) -- Liang Guo Thu, 20 May 2010 16:44:11 +0800 xserver-xorg-video-qxl-0.1.1/examples/0000775000000000000000000000000012233752225014603 5ustar xserver-xorg-video-qxl-0.1.1/examples/spiceqxl.xorg.conf.example0000664000000000000000000001036112233751142021710 0ustar Section "Device" Identifier "XSPICE" Driver "spiceqxl" # Enable regular port. Either this or SpiceTlsPort, or one of XSPICE_PORT or # XSPICE_TLS_PORT environment variables must be specified # Defaults to 5900. #Option "SpicePort" "5900" # Enable a TLS (encrypted) port. Either this or SpicePort must be specified, # either here or via environment varialbes or via xspice --port or --tls-port #Option "SpiceTlsPort" "5901" # Listen to a specific interface. Default is to listen to all (0.0.0.0) #Option "SpiceAddr" "" # Enable usage of SASL supported by spice-gtk client. Not required, # defaults to false. #Option "SpiceSasl" "True" # Do not request any password from client #Option "SpiceDisableTicketing" "0" # Set directory where cacert, server key and server cert are searched # using the same predefined names qemu uses: # cacert.pem, server-key.pem, server-cert.pem #Option "SpiceX509Dir" "" # Set password client will be required to produce. #Option "SpicePassword" "" # Set spice server key file. #Option "SpiceX509KeyFile" "" # Set cert file location. #Option "SpiceX509CertFile" "" # Set key file password. #Option "SpiceX509KeyPassword" "" # Set tls ciphers used. #Option "SpiceTlsCiphers" "" # Set cacert file. #Option "SpiceCacertFile" "" # Set dh file used. #Option "SpiceDhFile" "" # Set streaming video method. Options are filter, off, all # defaults to filter. #Option "SpiceStreamingVideo" "" # Set zlib glz wan compression. Options are auto, never, always. # defaults to auto. #Option "SpiceZlibGlzWanCompression" "" # Set jpeg wan compression. Options are auto, never, always # defaults to auto. #Option "SpiceJpegWanCompression" "" # Set image compression. Options are off,auto_glz,auto_lz,quic,glz,lz. # defaults to auto_glz. #Option "SpiceImageCompression" "" # Set to true to only listen on ipv4 interfaces. # defaults to false. #Option "SpiceIPV4Only" "" # Set to true to only listen on ipv6 interfaces. # defaults to false. #Option "SpiceIPV6Only" "" # 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" # If set, the Spice Server will exit when the first client disconnects #Option "SpiceExitOnDisconnect" "1" # Enable caching of images directly written with uxa->put_image # defaults to True #Option "EnableImageCache" "True" # Enable caching of images created by uxa->prepare_access # defaults to True #Option "EnableFallbackCache" "True" # Enable the use of off screen srufaces # defaults to True #Option "EnableSurfaces" "True" # The number of heads to allocate by default # defaults to 4 #Option "NumHeads" "4" # Set Spice Agent Mouse # defaults to false #Option "SpiceAgentMouse" "False" # Set Spice Playback compression # defaults to true #Option "SpicePlaybackCompression" "True" # Disable copy and paste # defaults to 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 is no mixing. #Option "SpicePlaybackFIFODir" "/tmp/" 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.1/examples/Makefile.am0000664000000000000000000000230512233750620016634 0ustar # 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.1/ChangeLog0000664000000000000000000042633412233751142014550 0ustar commit b8b67dc138092de488fb6ef62ce4bcb99aaa881a Author: Alon Levy Date: Sun Oct 20 17:59:07 2013 +0300 Release 0.1.1 commit 27cb65b1ad669789bebef356ea9086491dbed382 Author: Alon Levy Date: Sun Oct 20 16:33:39 2013 +0300 xspice: add tests for audio remoting Signed-off-by: Alon Levy commit 7d84ff3a11d61d0619ab3492097b493d08e97ad2 Author: Alon Levy Date: Sun Oct 20 16:32:54 2013 +0300 Xspice: kill Xorg process on SIGTERM Signed-off-by: Alon Levy commit 715707cf3038447138c5408e6ed91d788c7e37fa Author: Alon Levy Date: Sun Oct 20 15:36:30 2013 +0300 qxl_image: fix build break once MIN/MAX switched to spice-protocol In commit 5e122e4ab1ac35186cc610cd0d518cfd5e78d902 commit 48d762993653d1d88da4b93a646bafd844507f26 Author: Alon Levy Date: Sun Oct 13 15:39:43 2013 +0300 spiceqxl_audio: fix possible buffer overflow (clang) I've tested this. Previously strncat was used incorrectly, it is replaced with snprintf per Uri's suggestion. Signed-off-by: Alon Levy commit 2ed03db1ae39f26b4ba62a7db140a56f9acbe4bc Author: Alon Levy Date: Wed Oct 16 15:22:26 2013 +0300 Xspice: add --audio-fifo-dir Signed-off-by: Alon Levy commit a6ce6b285e6f4783b63cc0af309562771b516dfc Author: Alon Levy Date: Sun Oct 13 15:39:11 2013 +0300 uxa: use PIXMAN consts instead of PICT in some places (silence clang warning) Signed-off-by: Alon Levy commit 8afb354f094e253585b93108175793fe7bf13606 Author: Alon Levy Date: Sun Oct 13 15:38:35 2013 +0300 spiceqxl_display: add noreturn attribute (silence clang warning) Signed-off-by: Alon Levy commit 2f7e4c845366328e4f561654e0c38314c7aafe69 Author: Alon Levy Date: Sun Oct 13 15:38:16 2013 +0300 qxl_get_formats: use surface enum, not bitmap (fixes clang warning) Signed-off-by: Alon Levy commit 70884bd353c34c0be23c2b21eec320cd8c637f4f Author: Jeremy White Date: Thu Oct 17 14:08:06 2013 -0500 Use non deprecated functions, removes warnings from build. commit 5e122e4ab1ac35186cc610cd0d518cfd5e78d902 Author: Jeremy White Date: Thu Oct 17 14:07:29 2013 -0500 Remove MAX/MIN macros that are now included via spice/macros.h. commit 78f1115d11bea8be572e6959fa4db0313454b318 Author: Alon Levy Date: Mon Sep 2 17:40:20 2013 +0300 Xspice: vdagent{,d} launching support It checks for a new enough vdagentd/vdagent via the new -S command line parameter available via "vdagentd -h" and "vdagent -h". Signed-off-by: Alon Levy commit aa9d1cc75d494ae2d33ee2c925a35338ce85f4c2 Author: Alon Levy Date: Mon Sep 2 17:39:51 2013 +0300 Xspice: require display parameter, and set if earlier in environment Signed-off-by: Alon Levy commit dc451204ec5a4abceac977d99893918091324271 Author: Alon Levy Date: Mon Sep 2 17:39:11 2013 +0300 Xspice: correct doc string Signed-off-by: Alon Levy commit 9f4e429c13fd6e56a200d58c735b37d7eeb671fe Author: Alon Levy Date: Mon Sep 2 17:38:45 2013 +0300 Xspice: use subprocess.Popen, nicer cleanup of files/processes Signed-off-by: Alon Levy commit a6d00dccbf8257c0d225e0886cb2d4a8ba60fe38 Author: Alon Levy Date: Mon Sep 2 17:28:50 2013 +0300 xspice: add uinput support to vdagent support Signed-off-by: Alon Levy commit 294daff7ea930f338ec6d77ec3465b6f727c168e Author: Alon Levy Date: Mon Sep 2 17:32:16 2013 +0300 xspice: add vdagent support Adds a configurable virtio path used to communicate with the vdagentd, and a configuration variable for enabling the thing. With this you can have multiple monitors, but due to usage of a tablet you cannot generate pointer events on and monitors besides the first. clipboard already works. The next patch adds uinput emulation to let vdagentd generate uinput events and fix this glitch. Signed-off-by: Alon Levy commit 9d8a953c8c64cb3d896ad5ae9a534e6a5b71a2f5 Author: Alon Levy Date: Mon Sep 2 17:45:38 2013 +0300 qxl_option_helpers: add required includes Signed-off-by: Alon Levy commit cfdd8e58df1afd073abf6b0db804f3e10724317d Author: Alon Levy Date: Mon Sep 2 16:34:41 2013 +0300 spiceqxl_inputs: expose buttons & position api for agent usage Signed-off-by: Alon Levy commit 9f4c7aed3c75e4214cd16f28b890d6b99f6a2946 Author: Alon Levy Date: Mon Sep 2 16:32:22 2013 +0300 xspice: zero memory on allocation, fix uninitialized use (valgrind reported) Signed-off-by: Alon Levy commit 9bc5e720f20caf91f95c1fb56bc6ac8c24725eb8 Author: Alon Levy Date: Sun Sep 1 17:43:00 2013 +0300 xspice: support sending monitors config Signed-off-by: Alon Levy commit 751d51c3a5cb67d9d1e14d9fc43da20b226a99f3 Author: Alon Levy Date: Mon Oct 14 12:26:06 2013 +0300 fix undefined symbol qxl_enter_vt_kms for XSPICE commit d38b40046df43810ae79628b5424d7b621307795 Author: Jeremy White Date: Tue Oct 1 14:00:38 2013 -0500 Handle a missed case where lack of kms doesn't work with XSpice. commit 14dcb4016ec2f940dbef175b55875f0ac8c14f5c Author: Dave Airlie Date: Wed Sep 25 08:31:14 2013 +1000 fix build with --disable-kms Signed-off-by: Dave Airlie commit 86a1751ca6d6b08caa6f060b977f079a4f05b384 Author: Jeremy White Date: Thu May 23 09:23:22 2013 -0500 Provide compatibility for Xorg list code with Xorg < 1.12, for RHEL 6 support commit d261a3aef5a5d470057ffd9691af52c214681257 Author: Alon Levy Date: Sun Sep 8 14:51:38 2013 +0300 fix a bunch of warnings - unused variables, labels, code Signed-off-by: Alon Levy commit abe9cd20186fa810aaf880222d1c9ab3ab6bd379 Author: Dave Airlie Date: Wed Jul 3 15:28:10 2013 +1000 qxl: fix broken cursor hotspot on KMS driver bugzilla: http://bugzilla.redhat.com/974662 Signed-off-by: Dave Airlie commit 96e6be278896ea6ecb43984d7e6fe8eea3b75ab1 Author: Dave Airlie Date: Tue Jul 9 07:33:34 2013 +1000 qxl: drop GPL code from qxl driver This code was added due to lack of oversight on commits, remove it, this loses this feature for now, a clean implementation using MIT licensed code will be written later. Signed-off-by: Dave Airlie commit 8b03ec16acece636f96cf30362061716f6b596c2 Author: Cole Robinson Date: Tue Jul 2 16:19:13 2013 -0400 qxl: Report actual module version https://bugzilla.redhat.com/show_bug.cgi?id=813684 Signed-off-by: Cole Robinson Signed-off-by: Dave Airlie commit 131b7a87506adb304be9c479803d6952dce599cd Author: Dave Airlie Date: Mon Jul 1 13:21:13 2013 +1000 qxl: add uevent handler support This allows the driver to process uevents from the kernel when the mode changes. Signed-off-by: Dave Airlie commit 9075ac50655b1035275a4b79d6cfdc0de38dcca0 Author: Alon Levy Date: Thu May 30 14:45:27 2013 -0400 spiceqxl_main_loop: fix use of watch after removal rhbz 968931 Signed-off-by: Alon Levy commit 5376ed35a16516bcf97cc13666593d1518231d1f Author: Dave Airlie Date: Sat Jun 29 21:28:33 2013 +1000 qxl: fix issue with resizing dev_image improperly this could result in a segfault when resizing. Signed-off-by: Dave Airlie commit a391983e75d62bc1cfc2bab366ba983c2cd0954b Author: Dave Airlie Date: Tue May 28 13:53:06 2013 +1000 qxl: fix 32-bit mmaps 32-bit mmaps were broken, and I really thought I'd applied this before, but must not have made it across. Signed-off-by: Dave Airlie commit aead20f14a912879a297d77e85d9b029fd5f4e1e Author: Dave Airlie Date: Thu May 23 05:19:39 2013 +0100 qxl: don't enable kms unless we can find qxl_drm.h Reported-by: Jeremy White Signed-off-by: Dave Airlie commit 1b4d51be0502224c5b6c165b7aaec02d7e782c8c Author: Dave Airlie Date: Tue May 14 11:13:00 2013 +1000 qxl/kms: recreate the host image upon resize The code was just missing this, it was freeing stuff but never reallocating it, oops. This along with the kernel side fix should allow better randr support under KMS. Signed-off-by: Dave Airlie commit 3e37b2c38f661b0b8e285cfa7f0549aa3d216eb5 Author: Dave Airlie Date: Thu May 9 13:43:57 2013 +1000 qxl: add KMS support v1.2 Avoid DRI create busid symbol for now. fix warnings. commit 77a159451578d0e5773b2af6ed181ad076a6c7a5 Author: Jeremy White Date: Mon Apr 29 14:16:40 2013 -0500 Set -noreset on invocation commit 1263b3934c695e124d09a850a4530b17bddcbf51 Author: Jeremy White Date: Mon Apr 29 14:07:03 2013 -0500 Modify X interfaces to better enable Xorg resets. However, it still crashes, most likely due to the xorg_timer in the watch structure. Those timers become invalid at Xorg server reset (it clears all timers), but we go on to continue to use them. As fixing this fully will likely require some messy rework, simply documenting -noreset seems like the best choice for now. commit 11802306622f5580cd211aec175f9fef714e66e4 Author: Søren Sandmann Pedersen Date: Thu Mar 28 08:44:49 2013 -0400 When DebugRenderFallbacks is turned on, print debug spew in many cases Whenever we can't accelerate, and DebugRenderFallbacks is turned on, print out the reason we couldn't accelerate. commit 6b5df7bb359b548b65033b16ac539bf3dd91793a Author: Søren Sandmann Pedersen Date: Thu Mar 28 08:44:48 2013 -0400 Add new DebugRenderFallbacks option This option defaults to off. When enabled, uxa_set_fallback_debug() is set to true. Later on more debug information may be turned on conditional on this option. commit 3cbd14b93925c70f32a132082a8eae59380ec4bf Author: Jeremy White Date: Wed Mar 27 13:28:57 2013 -0500 Make the Deferred FPS mode available in all cases, not just XSPICE. commit f6aebb149e4ddffe303f5cfdeb8b24c79416db60 Author: Jeremy White Date: Thu Mar 21 12:58:12 2013 -0500 Eliminate gcc warning "initialization discards 'const' qualifier from pointer target type" commit 742a032bcd58534631095dafa89d24c076c43ffe Author: Jeremy White Date: Sun Sep 9 10:50:19 2012 -0500 Eliminate a gcc "cast discards '__attribute__((const))'" warning commit eca99cb6827511a0750e21a09f3f00043966dcc4 Author: Jeremy White Date: Thu Mar 21 10:44:46 2013 -0500 Actually request the forcibly inline functions to be inline. Silences a gcc warning (-Wattributes). commit 48170807029bf0a13f60b2d89047474b05168c84 Author: Jeremy White Date: Sun Sep 9 11:14:14 2012 -0500 Eliminate a printf format warning on 32 bit systems. commit 8f8f600719cae19b2bc3c5afa7dfd7f4d1c3ac19 Author: Jeremy White Date: Sun Sep 9 15:27:14 2012 -0500 Document a meaningful warning. commit 60e1dc6fb66c23b3bfb8511073866a468a73a599 Author: Jeremy White Date: Thu Mar 21 10:33:01 2013 -0500 Removed unused local variables commit 274a08685b3bf709cd08a74c082f1253ee46d43a Author: Jeremy White Date: Sun Sep 9 10:34:52 2012 -0500 Eliminate gcc warning on duplicate use of ARRAY_SIZE by Xorg. commit 42c88ddaccd3a286c6637aa82248e5eb1cc75006 Author: Jeremy White Date: Wed Mar 20 15:00:15 2013 -0500 More correctly signal that we only want the first head connected at start. This prevents a bug when using old versions of qemu (< 1.2) and the latest driver version. This was uncovered when I found that my patch beccf8e8: Establish a preferred default of 1024x768 correctly. causes a failure when running with the qemu in Debian. The video ram allocated by older qemu versions (8M) is not enough to hold the default heads (4) times the default screen size (1024x768). It worked prior to my patch because the previous code was a bit of a mess. It would hard code 1024x768 for Virtual Size, hack some crtcs to disabled, and sorta kinda have 4 heads connected. Later resizes would come through and mostly fix things up, so we largely got away with. This change should correctly signal that we start with only one head connected. There is still more that could be done with the randr code; we have unimplemented functions that I suspect would allow us to fail more graciously when the user tries to create displays in excess of our RAM. commit 475f3b73345dd0bc480f0761e41a35e7ce0c80b8 Author: Jeremy White Date: Tue Mar 19 08:47:32 2013 -0500 Add an example stanza for the FIFO dir logic. commit 9f18384a9b34da4eed3690f11575b6633c61f1a0 Author: Andrew Eikum Date: Fri Mar 15 09:19:49 2013 -0500 Implement sending audio to the client from a directory of FIFO queues This introduces a new Xorg.conf option, SpicePlaybackFIFODir, which will be monitored for files. The XSpice driver will mix and forward the audio data sent to those pipes to the Spice client. This is designed to work with PulseAudio's module-pipe-sink, but should work with any audio output to a pipe. For example, use with this PA configuration option: load-module module-pipe-sink file=$FIFO_DIR/playback.fifo format=s16 rate=44100 channels=2 making sure the format, rate, and channels match the Spice protocol settings. commit 56623b13a860a9b76ca59d5e9e7d14c80d72d245 Author: Jeremy White Date: Thu Mar 14 12:47:13 2013 -0500 Add a --xsession option, to allow for an automatic start of an xsession script commit d540f7e91e39db6eec752e698252b6efb83ed950 Author: Jeremy White Date: Thu Mar 14 11:30:39 2013 -0500 Enable the deferred-fps and exit-on-disconnect options commit 6d480421cd307e4cce91e6f6da85890715c8d676 Author: Jeremy White Date: Thu Mar 14 10:50:44 2013 -0500 Add a '--auto' flag to create a temporary xorg.conf file. commit 7038ec9eb6bdff60623f38462376acb2de1e2272 Author: Jeremy White Date: Thu Mar 14 10:20:03 2013 -0500 Consolidate and document the missing parameters commit 60d9b28cc02e2560b7a96826be5283df6014fb14 Author: Jeremy White Date: Wed Mar 13 14:43:09 2013 -0500 Add missing options to the example xorg.conf file. Poor man's documentation for the win! commit 45a862ef5c75a913628155ed65344ad4f3bc94a0 Author: Jeremy White Date: Wed Mar 13 14:25:45 2013 -0500 Handle cache and surface options the same way we handle all options. Add comment lines for them to the example xorg.conf file. commit cee2fe80480db851402186096f8fc433306d330e Author: Jeremy White Date: Wed Mar 6 12:30:36 2013 -0600 Initialize our update region at surface creation, avoids 0 size regions commit c36be89ea4f5bbfb81bc2eee4af130d2e9553e7e Author: Jeremy White Date: Wed Mar 6 12:30:01 2013 -0600 We're uploading a drawable from the primary surface, use it. commit 7a7b12762904eea329f678987565d89db56421b6 Author: Dave Airlie Date: Tue Mar 12 10:27:19 2013 +1000 qxl: drop pointless and wrong memset. Signed-off-by: Dave Airlie commit a474a718e118895978beb19d77d730f578c201ef Author: Dave Airlie Date: Wed Mar 6 13:54:22 2013 +1000 qxl: use u64_to_pointer to avoid warnings when building on i686 Signed-off-by: Dave Airlie commit 5d73de5e250350e10d0dc8b000ce1d3a21eeb2b5 Author: Dave Airlie Date: Wed Mar 6 13:33:50 2013 +1000 qxl: fix xspice build due to missing include. Signed-off-by: Dave Airlie commit 6400c31454483aa2a8edd828579d9a2e7190df1c Author: Dave Airlie Date: Wed Mar 6 13:31:30 2013 +1000 qxl: fix build in koji Signed-off-by: Dave Airlie commit 9d45cc50c0f7302a649a5ae5b850f0bdbb9a2771 Author: Dave Airlie Date: Tue Feb 26 15:03:37 2013 +1000 qxl: separate surface ums code out into separate file All the surface cache code is UMS specific, so separate most of it out. Signed-off-by: Dave Airlie commit 8c7fada8f42cc4a741f7f1d210b50f754ef8805e Author: Dave Airlie Date: Tue Feb 26 14:03:40 2013 +1000 qxl: abstract object allocation away from surface/image/cursor code This abstracts the object bo/surface allocation away from the user code. The idea is we can then plug in a KMS backend for this abstraction and mostly keep the surrounding code intact. This is probably the biggest change to the driver in terms of impact of KMS code on UMS code. At the moment I'm storing linked list of bos for release handling, and that might be better done with a different data structure, since we are looking them up by phy_addr, either a hash table or some sort of rb tree perhaps, rather than a linear search. This commit also starts usign the xorg list macros, which may require some compat code to work in history. Signed-off-by: Dave Airlie commit f0e9d7bb9658a89909f97b485437a1375aa0267f Author: Dave Airlie Date: Tue Feb 26 13:23:47 2013 +1000 qxl: add direct pointer from qxl surface to qxl screen This is prep work to allow the cache to be bypassed for kms. Signed-off-by: Dave Airlie commit edea5bed01049ae9f62278fe998b13745c8ffa8c Author: Dave Airlie Date: Tue Feb 26 13:11:08 2013 +1000 qxl: pass surface struct instead of ids in drawable functions. this just changes it so we pass the surfaces not just the ids. Signed-off-by: Dave Airlie commit 8b27ab5b956a0e4ca1dd14f26634d0159d0976dc Author: Dave Airlie Date: Tue Feb 26 13:07:39 2013 +1000 qxl_edid: add missing config.h Signed-off-by: Dave Airlie commit 37bc463a553c03ea9a757ad2ebea056b42c6e8ad Author: Dave Airlie Date: Tue Feb 26 13:04:20 2013 +1000 qxl: split common pre init code out from qxl pre init This just moves the options parsing and color setup calls to a separate function. Signed-off-by: Dave Airlie commit 9a1914cff1200a58dab1edb939292f0fb96ec025 Author: Dave Airlie Date: Tue Feb 26 12:49:53 2013 +1000 qxl: fix memory leak on driver exit. We were leaking two allocations on driver exit. Signed-off-by: Dave Airlie commit e24b9ca438c17797012a48f9647682d382e44de0 Author: Dave Airlie Date: Tue Feb 26 12:48:36 2013 +1000 qxl_surface: move to using struct to avoid ordering issues with typedef. Signed-off-by: Dave Airlie commit 39b5c10ac426db8377389433d4d04f54c8b710ef Author: Dave Airlie Date: Tue Feb 26 12:47:33 2013 +1000 qxl: pass qxl instead of surface cache to some functions This is more preparation work for adding bo abstraction layer. Signed-off-by: Dave Airlie commit b13a5e7cfead7d4de4d24d86f0eabeae76934fb9 Author: Dave Airlie Date: Tue Feb 26 12:26:45 2013 +1000 qxl: split surface struct out into its own header file. This is just ground work for kms addition. Signed-off-by: Dave Airlie commit 2753e5bf4cc6cdfaea50785cc70285a1bb995fcf Author: Dave Airlie Date: Tue Nov 20 12:43:27 2012 +1000 uxa: port over tiled fill code from EXA This makes X -retro actually render fast, as opposed to glacially slow. Signed-off-by: Dave Airlie commit 3dfb0a968d572e8af2bc52c964beeaaab4675448 Author: Dave Airlie Date: Tue Nov 13 14:43:45 2012 +1000 qxl: move mspace setup to qxl_mem.c removes mspace stuff from main qxl_driver.c Signed-off-by: Dave Airlie commit 55c0b36c54fe6dfb25cdbcfad0add9707c4a734b Author: Dave Airlie Date: Tue Nov 13 14:40:53 2012 +1000 qxl: move garbage collector + alloc into qxl_mem.c This moves a lot more code out of the qxl_driver.c file. Signed-off-by: Dave Airlie commit a604a002fda045c8166333d4f17925280844d26d Author: Dave Airlie Date: Tue Nov 13 14:36:58 2012 +1000 qxl: move mem slots code to qxl_mem.c This moves the mem slot setup code to qxl_mem.c. Signed-off-by: Dave Airlie commit 338cfb89bb3f1e3ae4cbfdbf4c9640dde9707f5d Author: Dave Airlie Date: Tue Nov 13 14:35:05 2012 +1000 qxl_mem: drop unused if 0 code. Doesn't look we want this code anymore, so drop it. Signed-off-by: Dave Airlie commit 120ca968123e5c1e9d355bc43826a92fdfdf7f2c Author: Dave Airlie Date: Tue Nov 13 14:33:39 2012 +1000 qxl: move io functions to a separate file this moves all the lowlevel ioport interaction code to a separate file. Signed-off-by: Dave Airlie commit 43ff8209da8ca3770339bbc9b776e114b94a55a7 Author: Dave Airlie Date: Tue Nov 13 14:20:10 2012 +1000 qxl: split user modesetting code and crtc code out. This splits the UMS modesetting and xf86Crtc code out into a separate file. Signed-off-by: Dave Airlie commit 5f8e250927ebaf8a76621efb9294e527a7bde99b Author: Dave Airlie Date: Tue Nov 13 14:04:17 2012 +1000 qxl: split uxa related code into separate file qxl_driver.c is insane, it needs to be split out like other X.org drivers. This is step one, move the uxa interfacing code into its own file. Signed-off-by: Dave Airlie commit beccf8e81ea6bb4c86bcaf3cd4aac5e18f6d3f0d Author: Jeremy White Date: Tue Jan 29 13:34:52 2013 -0600 Establish a preferred default of 1024x768 correctly. This fixes a bug with x-spice where you could not specify a default mode in an xorg.conf modeline that was greater than 1024x768. This also eliminates (and partially reverts) patch c1b537fc. It also fixes bug 894421, where gnome modes flicker and work poorly. commit c90bb813f3d30c8c800b1f56d77713cce417f1b2 Author: Uri Lublin Date: Thu Jan 17 16:26:35 2013 +0200 qxl_driver: monitors_config: adjust to memory-remap Resolves: rhbz#883578 Call qxl_allocate_monitors_config upon memory-remap such that qxl->monitors_config points to the start of monitors_config segment in qxl RAM memory. Currently after memory remap, it's possible that monitors_config memory and video-memory (or graphics) overlap, which means that one may overwrite another. Specifically in the bug above, monitors_config value are being overwritten by video pages, and on the destination bad values are read which cause problems on the server and client. It may be a good idea to add some protection on the server side, e.g. calcluate checksum, compare values against modes, or limit ->count and ->max_allowed and ignore bad monitors_config values Also do not memset-0 monitors-config upon allocation (remapping) to not overwrite likely good configuration (in case it is being read by the host, e.g. upon migration). commit dadc1ad3f05633f926c9cb75a93367c5d52af988 Author: Uri Lublin Date: Thu Jan 17 16:26:34 2013 +0200 qxl_driver: simplify calling qxl_update_monitors_config Simplify by calling check_crtc from within qxl_update_monitors_config, instead of calling check_crtc before every call to qxl_update_monitors_config. commit 3963ad1a140ef4b2a3f09471d0135311b9b36c87 Author: Uri Lublin Date: Thu Jan 17 16:26:33 2013 +0200 qxl_driver: check_crtc: handle qxl->crtcs == NULL commit 1d5001116662ef832e010a098a68b81446c1c531 Author: Uri Lublin Date: Thu Jan 17 16:26:32 2013 +0200 qxl_pre_init: fix calculation of available video memory Don't forget to substract monitors_config area. Memory map of qxl RAM looks something like the following: --------------------- | | | Surface0 Area | | | --------------------- | | | | | Video Pages | | | | | --------------------- | Monitors Config | --------------------- | RAM Header | --------------------- commit 2e003fc6447128ca1035061a3f98fd57ee3bed24 Author: Uri Lublin Date: Thu Jan 17 16:26:31 2013 +0200 qxl_driver: remove unused enum ROPDescriptor commit 4908d6c4d13014f38dadfd1020cd680e81f053b7 Author: Jeremy White Date: Thu Sep 6 16:01:15 2012 -0500 Enable the use of SpiceExitOnDisconnect so that Xspice can be configured to allow exactly one server per client session. commit 6677781531a8638c3813ee091881271d7d7e23b2 Author: Jeremy White Date: Thu Sep 20 16:04:08 2012 -0500 Implement prepare_copy using GC functions. This avoids having to screen out bad alu and planemasks; we let the X driver take care of it. commit 6130831c7341acee332e1c0dd7d3b74350e87fcc Author: Jeremy White Date: Thu Sep 20 15:52:31 2012 -0500 Implement copy with fbFill. This fixes issues with 1 bit images; some pixman implementations did not support 1 bit images - the general fbFill implementation takes care of it for us. commit 531a844ab9136d657f90a30bb049068e92629c65 Author: Jeremy White Date: Tue Sep 11 12:56:19 2012 -0500 Add a deferred frames mode. This renders all operations to a frame buffer, and sends updates periodically. commit 4301bff52bfec982b16896e395d9b4785c359a73 Author: Jeremy White Date: Mon Sep 10 12:29:29 2012 -0500 Shift the uxa function initialization to a separate function. commit 7d62dcc6d400e3f96fb7921fbdb852f3b9024b17 Author: Jeremy White Date: Mon Sep 10 10:36:39 2012 -0500 Add a DeferredFPS option. commit a44be4fd011cc0b80d0c8109589e7aa1c26cad1e Author: Jeremy White Date: Thu Sep 13 09:53:03 2012 -0500 Unify memory management for the primary screen. We never actually connected the host_image pixmap to the primary screen, because we never operate on the bits of the primary screen. This causes problems if we wish to use traditional X operations to modify the primary screen, as they will fail and corrupt memory as they go. commit 819b1f62efbecf33db7bb0b4402a798262ae4574 Author: Adam Jackson Date: Wed Jan 9 22:55:47 2013 -0500 Remove mibstore.h Signed-off-by: Adam Jackson commit b211ff37e1fdca853275e73e5cd089c0d713f521 Author: Yaakov Selkowitz Date: Fri Sep 28 14:06:07 2012 -0500 Only include Xv headers if server supports it Signed-off-by: Yaakov Selkowitz Reviewed-by: Søren Sandmann Pedersen commit 29ac84b5009ba83345200c1ca4511c2c86331624 Author: Yaakov Selkowitz Date: Fri Sep 28 13:35:33 2012 -0500 Add XORG_LIBS to LIBADD This affects only Cygwin (on which only spiceqxl is supported), where drivers must be linked against the Xorg implib. On other systems, XORG_LIBS will be empty. Signed-off-by: Yaakov Selkowitz Reviewed-by: Søren Sandmann Pedersen commit 10d6e8ee3e59655c32c2d518cc6686eff902c6dc Author: Jeremy White Date: Wed Aug 8 14:58:36 2012 -0500 Change include paths to fix rare compilation issue. Technically, the xorg/ prefix should not be specified. It generally works, because xorg/ is usually hung off /usr/include. This enables compliation that correctly respects a pkg-config --cflags xorg-server. commit d8bb331784792bfd35bf158875b434243f0fe019 Author: Søren Sandmann Pedersen Date: Thu Sep 20 19:11:55 2012 -0400 Version bump to 0.1.0 commit 6520293e1e1f57bafbcf99592e766f810ce3ad2d Author: Søren Sandmann Pedersen Date: Thu Sep 20 16:26:36 2012 -0400 Make the checks for composite and a8 return FALSE for Xspice They depend on the PCI revision which is not available for Xspice. commit 7f0b820d919eb944eae201de03b186bd247b0324 Author: Jeremy White Date: Wed Sep 12 10:38:41 2012 -0500 Only use dixScreenSpecificPrivatesSize if we have a new enough Xorg. This lets us continue to support older Xorg releases. This reverts 4f37cd85 and partially reverts 4a43bd4. commit fb038eb37906eba9a88e0cb6622a59f06dcc2a68 Author: Søren Sandmann Date: Wed Sep 12 12:54:06 2012 -0400 Don't issue composite commands or create a8 surfaces when not available When the device or the client are not capable of composite commands or a8 surfaces, don't issue these commands. commit 4f37cd854f128714b8fcf3c0ab8afd72986407a0 Author: Johannes Obermayr Date: Fri Sep 7 18:05:21 2012 +0200 Require XServer >= 1.12.99.901 in conjunction with commit 4a43bd4. commit 7059cff787eef80f3d3345de705e912b292a9f97 Author: Søren Sandmann Date: Thu Jul 12 09:53:31 2012 -0400 Add Render support This commit adds support for using the new Composite command in spice protocol 0.12.0. This command is similar to the Composite request in the X Render protocol. By implementing the UXA composite stubs, we get acceleration for most common Composite requests, including glyphs. commit 2fecf3a171e64ca0dad5653ed740409dc5af2edf Author: Søren Sandmann Date: Tue Dec 20 09:01:17 2011 -0500 Enable 8 bit pixmaps. a8 surfaces are now supported with the 8BIT_A format in spice, so we can have support 8 bit pixmaps. commit 37c97620da959d9214abfc760835aa3031d54daa Author: Søren Sandmann Date: Sun Aug 12 11:03:16 2012 -0400 Use an RGBA format for 32 bit images With the upcoming Render changes, we can no longer assume that the fourth channel of images is unused. commit 1e89aab2dc0beb01f43a2397faa05a8cd01a7547 Author: Søren Sandmann Date: Wed Aug 22 15:27:09 2012 -0400 uxa: Plug leak in uxa_glyphs_via_masks If prepare_composite() fails, we need to free the temporary mask before returning. commit afd8d20b84a4b4e9b22483e379d594517333e8c7 Author: Søren Sandmann Date: Tue Aug 14 14:20:11 2012 -0400 uxa-glyphs: don't prepare composite when dest is not offscreen It is possbible for a pixmap to not be in video memory after uxa_clear_pixmap() was called. When this happens, we need to destroy the pixmap and return 1 to indicate that the operation can't be accelerated. commit e738d00e1fb3cd469f850765e2b42976c2a85764 Author: Søren Sandmann Date: Tue Aug 21 12:56:52 2012 -0400 Improved support for memory debugging. Make all memory allocation functions take a string that will explain what the memory will be used for. This allows debug print statements to be added to the allocation functions and could later potentially be used for more detailed statistics. commit 4a43bd436c58dae72f91905657a36158efc68907 Author: Alon Levy Date: Thu Aug 23 18:28:03 2012 +0300 src/qxl_driver: use the new dixScreenSpecificPrivatesSize xserver introduces a new screen specific privates infrastructure, moving the PRIVATE_PIXBUF over there, breaking qxl that was using the wrong dixPrivatesSize to access it - there is a new array of screen specific/not flags, and PRIVATE_PIXBUF is screen specific. xorg-xserver commit: 9d457f9c55f12106ba44c1c9db59d14f978f0ae8 This fix breaks backward compat. The next release will only work with xorg-xserver >= 1.12.99.901 RHBZ: 844463 commit 0998bf63cd6c69a98f67094eba4e4edd2bf2a906 Author: Søren Sandmann Pedersen Date: Thu Aug 23 17:24:51 2012 -0400 Coding style fixes Undo most of the damage from 7f8d3ed05cbe891 commit 5554358fa3baf6616159b65f2f65a138f401aaf8 Author: Søren Sandmann Pedersen Date: Thu Aug 23 17:22:58 2012 -0400 Bump spice-protocol version requirement to 0.12.0 commit a261befde42d21f3fa9e4a56f121203cc67f3831 Author: Alon Levy Date: Thu Aug 23 17:27:20 2012 +0300 qxl_driver: Xspice: fix build breakage commit c1b537fcafc3b9121d8c79cf5e98566158f34ab6 Author: Marc-André Lureau Date: Sun Jul 15 09:38:55 2012 -0400 Return a preferred mode matching the current mode This make gnome-settings-daemon not switch resolution automatically to the largest available. commit ef4f158a18f03444d358b5ece39db636f76365f9 Author: Marc-André Lureau Date: Fri Jul 13 08:20:33 2012 -0400 Change output status when update monitors config commit 907d0ff0b0f6934d37fbab4a6889295d70ae3496 Author: Marc-André Lureau Date: Sun Jul 8 15:35:23 2012 -0400 Bypass rrcrtc.c screen size bounds check commit 557e23e82b6d738fab6fa6bd28b077dd81437b22 Author: Marc-André Lureau Date: Fri Jul 6 08:06:10 2012 -0400 Avoid calling qxl_update_monitors_config() with invalid config During startup, the monitors are not yet enabled/set. and we can avoid sending invalid/transient config. commit 47b54dcfd9ba2aa78e0f154a288321bb0ff98dce Author: Marc-André Lureau Date: Fri Jul 6 08:05:11 2012 -0400 Split crtc_set_mode_major() Avoid sending many monitor config changes during qxl_create_desired_modes() commit 6ee541636b6ed6846074afeb1c32d9e8e6a9bdda Author: Marc-André Lureau Date: Fri Jul 6 08:02:21 2012 -0400 Add check_crtc() helper, to catch crtc/monitor config errors commit 7f8d3ed05cbe8914910e49b49ec44df15562bd16 Author: Marc-André Lureau Date: Wed Jul 4 14:56:07 2012 -0400 Coding style improvement commit f9980eec2287a01a918ff073a45f13d9dc66a1c6 Author: Marc-André Lureau Date: Wed Jul 4 14:01:45 2012 -0400 get rid of one of the gcc warnings commit 6c63d799a2b0f69485938dbae7975bf913284a23 Author: Alon Levy Date: Wed Jun 27 15:53:41 2012 +0300 qxl_driver/qxl_initialize_x_modes: remove modes not fitting framebuffer commit f493653a5e09ae0e0fe1a5f70c3aba1dc9fe86fe Author: Alon Levy Date: Mon Jun 25 17:09:20 2012 +0300 qxl_driver: add infra for surface0 resizing Most importantly, don't allow randr resize if it is too large for the currently allocated mspace. Ifdeffed out almost working code for reallocating the primary mspace (qxl->mem). commit 75619d076bb029b76bed7a885864e191c386aa23 Author: Alon Levy Date: Wed Jun 27 12:11:41 2012 +0300 add qxl_edid Taken from Virtual Box, following exactly the same logic: gnome-settings-daemon relies on the serial given in the edid to set the resolution to the same one last used on that screen. Since this is not what we want with a virtual machine, we produce a serial that is different for every resolution. commit de547245e75ea88f780205da072967bff119a636 Author: Alon Levy Date: Mon Jun 25 16:13:29 2012 +0300 (for later) qxl_ring: add helpers (debugging/unused) commit b4e3f07474c919af0e484a71fb86c788a703ad8c Author: Alon Levy Date: Mon Jun 25 16:20:31 2012 +0300 qxl_driver: qxl_init_randr: limit width/height to 8192, real check done on randr screen resize callback commit d85e07921dda5e5401a336d415e8deee4b663252 Author: Marc-André Lureau Date: Wed Jul 4 11:30:58 2012 -0400 Add some error message if pci revision < 4 commit e6bfdd035f3692b712496069b9361d010526802a Author: Alon Levy Date: Wed Jun 27 14:33:46 2012 +0300 qxl_driver: introduce qxl_crtc, crtc private Additionally prevents disabling of the primary crtc. commit f106ea65cde61c8f3cf5819d8c127fb7912ec067 Author: Alon Levy Date: Mon Apr 30 18:35:59 2012 +0300 qxl_driver: implement randr, arbitrary resolution, multiple monitors (big dump) Send a MonitorsUpdate - this should definitely be split into it's own patch. Require revision 4 - this is needed just for MonitorsUpdate, should go with it. Adds new config: OPTION_NUM_HEADS, defaults to 4. commit 21e5719f74749fa6187539dc0805cb12759d66da Author: Alon Levy Date: Thu May 24 09:56:06 2012 +0300 qxl_surface: add download_box_no_update commit 26b8b350628ec053217715e7a9cb77ff56e4f56e Author: Alon Levy Date: Thu May 24 09:55:27 2012 +0300 qxl_surface: normalize surface_send names, add option to surface_destroy to send/not send destroy message commit 1db72fdf45a3ebbf7f0687671c1def75309b9499 Author: Alon Levy Date: Wed May 23 20:44:51 2012 +0300 spiceqxl_io_port: mark FLUSH_SURFACES_ASYNC as unimplemented, do half of it commit 5d78c4cac24ad3830dba1900d5d879551dcbb407 Author: Alon Levy Date: Wed May 23 21:23:09 2012 +0300 qxl_screen_init: move uxa_resources_init down to after qxl_switch_mode has run commit f246a251f88dab6a9db83e2d8bbd699e29950459 Author: Alon Levy Date: Wed May 23 21:20:23 2012 +0300 qxl_close_screen: disable fb access, prevent segfaults in uxa later on I don't have a stacktrace to show any segfault unfortunately. commit 6dedd9a3f997c0112e71cfd0b28465348dfbbc36 Author: Alon Levy Date: Mon Jun 25 17:08:11 2012 +0300 qxl_driver: move mspace_set_*_func earlier commit f838df1589c63fba8e99200238cf0b4e3acac6b1 Author: Alon Levy Date: Tue May 29 17:24:50 2012 +0300 fix two segfaults in qxl_free_surface Both results from ProcFreePixmap being called in unanticipated circumstances: cache->all_surfaces is NULL surface->host_image is NULL To reproduce the following scripts work, in tandem: create xterms, destroy them chvt ============ xterm_test ============ import os import subprocess import time import atexit env = os.environ env['DISPLAY'] = ':0.0' xterms = [] def kill_all(): print "killing xterms" for x in xterms: x.kill() del xterms[:] atexit.register(kill_all) while True: for i in range(10): xterms.append(subprocess.Popen(['xterm', '+u8'])) time.sleep(1) kill_all() ============= chvt_test_helper ============ XPID=`pgrep Xorg` XTTY=`find /proc/$XPID/fd -lname "/dev/tty*"` XTTY=`readlink $XTTY` XTTY=${XTTY#/dev/tty} echo "chvt 1 (from Xorg)" chvt 1 sleep 2 echo "chvt $XTTY (to Xorg)" chvt $XTTY ============== chvt_test ================= while true; do ./chvt-test ; sleep 3; done commit c60681561c9cf8317be4e84ac8ac2bb465e13eb0 Author: Alon Levy Date: Mon Jun 11 10:13:39 2012 +0300 qxl: remove qxl_screen_t->stride commit 203f579fed5e0c8a9970bfabebc84c7d84578ee5 Author: Alon Levy Date: Wed Jun 27 12:07:16 2012 +0300 spiceqxl_inputs: don't hide the pointer global typedef commit 5706a3765bead75999f6d0ca0b17fed6c30ad7ef Author: Alon Levy Date: Wed Jun 27 11:06:17 2012 +0300 uxa: fix bad argument type from removed index API change (s/SCREEN_ARG_TYPE/SCRN_ARG_TYPE/) commit ea676f67d4c8fc863a711fb8630c1afccfa0561c Author: Alon Levy Date: Wed Jun 27 12:19:01 2012 +0300 whitespace fix commit de66207883efc1f32e96907c3e64f17b2bdf6c3e Author: Alon Levy Date: Sun Jul 8 14:05:08 2012 +0300 qxl_driver/qxl_switch_mode: destroy is not idempotent commit 6267b1a56f6104409fcb970eddc4ea9606421331 Author: Alon Levy Date: Wed Mar 7 14:30:58 2012 +0200 spiceqxl_display: reformat & rephrase Xspice comment commit e0f301fc0512502542573b3f8dd9452f5a7ea6e1 Author: Jeremy White Date: Wed Jun 13 17:04:12 2012 -0500 Compute totalPixmapSize using the same logic as in dix/pixmap.c, rather than hard coding 100. This was found while building with a modified X server; one with a PixmapRec size of 224, not 64 :-/. commit 6832c0fd917556c52f56f8e82706a83942ed3dc1 Author: Jeremy White Date: Sun Jun 3 10:28:05 2012 -0500 Actually process write watches in the wakeup handler My apologies for the churn; this is, I think, a slightly better patch than my previous patch, 'Process watches even when there is no X activity', in that it avoids doing an extra polling select when we're idle. commit 72a0def8114073c0051f3df880f731d3968cb344 Author: Alon Levy Date: Wed May 30 13:44:40 2012 +0300 qxl_switch_mode: don't evacuate, just recreate primary surface In summary, on vt enter we still: reset recreate memory slots clear our mspace allocators and then do what switch mode below says On vt leave we still: reset (this is redundant since the first VGA access will trigger a reset on the device side) On switch mode however we only: destroy primary surface create primary surface (different size) commit 8df3eba368e80f60ce815300b85a567a9b02141c Author: Alon Levy Date: Thu May 31 13:04:01 2012 +0300 qxl_surface: don't unlink surface 0 The primary surface, i.e. qxl->primary, the only surface with id==0, is allocated in qxl_surface_cache_create_primary with prev==next==NULL. Unlinking it was producing a wrong cache->free_surfaces == NULL. This was not a problem because unlinking the primary only happened in switch_host, which then called surface_cache_init. In a following commit switch_host is simplified to destroy-primary+create-primary, so this bug needs to be fixed first to avoid leaking surfaces and reaching a no surface available situation. commit 22157d4750f9090927d2e3473aa3d3a4f5232792 Author: Alon Levy Date: Thu May 31 13:03:54 2012 +0300 qxl_surface: add DEBUG_SURFACE_LIFECYCLE helpers commit 0f817bb4e1b4e33ef50c0399c92f1cc091840ef5 Author: Alon Levy Date: Tue May 29 12:25:12 2012 +0300 io: add qxl_io_destroy_primary commit 326b80974b4080ed7519801f7d1c96077f5ae0b9 Author: Alon Levy Date: Wed May 23 20:50:46 2012 +0300 prefix io with qxl_io, add several commit 73981e02c90cc81dd462f9fc2a00b5b11a9eab00 Author: Alon Levy Date: Wed May 23 20:52:48 2012 +0300 qxl.h: add device_primary tri state UNDEFINED/NONE/CREATED commit b600edc48270a4a368add11ec02e6d365d5da60d Author: Alon Levy Date: Thu May 24 00:08:38 2012 +0300 qxl_surface: logging: add function name to ErrorF commit 3a87e765d91a26ead2cfc5ddad1ba4f3e7d21922 Author: Alon Levy Date: Thu May 24 11:00:49 2012 +0300 qxl_surface: cosmetics commit 60478640a6c4d74c44fdf67350be6e180960cf5f Author: Alon Levy Date: Wed May 23 21:23:26 2012 +0300 qxl_pre_init: memset qxl struct commit 9d929ae1d1bb2e7f03221fcc4d70e761b6ff9242 Author: Alon Levy Date: Wed May 23 20:46:34 2012 +0300 qxl_driver: abort on mspace error, don't spin (default abort function) commit 6aa3ceb2d3f25726424b03a68ef949deadf7125a Author: Alon Levy Date: Wed May 23 20:44:06 2012 +0300 mspace: add mspace_malloc_stats_return commit 5e505dc6572ee29d0ebe912a8160a8e09bfb5d3e Author: Alon Levy Date: Tue May 29 13:46:34 2012 +0300 qxl_leave_vt: change outb to ioport_write (easier to grep / breakpoint on a single point) commit 67f86dc1e824d00a06bdc51ba4c3e88cfbd82292 Author: Alon Levy Date: Tue May 29 13:45:29 2012 +0300 rename qxl_reset to qxl_reset_and_create_mem_slots commit 21c1d576925e561551b91b44b0d286f0bdc689c4 Author: Alon Levy Date: Wed May 23 20:54:44 2012 +0300 qxl_mem: add debug flags, simple accounting and valgrind enabled adds preprocessor definitions DEBUG_QXL_MEM & DEBUG_QXL_MEM_VERBOSE commit a313b5ef1b5b6dda1e6c0ab47f458d692a5462f7 Author: Alon Levy Date: Wed May 30 10:46:54 2012 +0300 qxl_surface: handle destroyed pixmaps while evacuated Prevent access to freed memory when: 1. qxl_leave_vt/qxl_surface_cache_evacuate_all freed cache->all_surfaces 2. ProcRenderDispatch/damageDestroyPixmap/qxl_destroy_pixmap/qxl_surface_kill access a surface that pointed inside the all_surfaces array Solution in this patch: 1. never free all_surfaces 2. add an 'evacuated' field per surface, initialized to NULL, set during evacuation. 3. on qxl_surface_kill, if surface->evacuated is set, don't destroy the surface (it is already destroyed by this point via a reset in qxl_surface_cache_evacuate_all's caller, qxl_leave_vt), just unref the host pixmap, free the evacuated_surface_t and unlink it from the evacuated linked list, so it isn't recreated later on qxl_surface_cache_replace_all. commit c47ebff71878458ff6157aec7252999a6578fb97 Author: Alon Levy Date: Wed May 30 10:09:47 2012 +0300 qxl_driver: hide cursors on vt switch This is not enough to prevent any qxl_destroy_pixmap call during vt switch, but it prevents those triggered by CursorDisplayCursor. Note: a matching xf86_show_cursors call doesn't hurt, but is not required, so not adding it. It is still possible to access freed memory by the following trigger: ==4416== Invalid read of size 8 ==4416== at 0x5D15EC1: unlink_surface (qxl_surface.c:685) ==4416== by 0x5D162F9: qxl_surface_kill (qxl_surface.c:799) ==4416== by 0x5D12688: qxl_destroy_pixmap (qxl_driver.c:928) ==4416== by 0x55730B: damageDestroyPixmap (damage.c:1556) ==4416== by 0x51C77B: ShmDestroyPixmap (shm.c:273) ==4416== by 0x54591B: FreePicture (picture.c:1465) ==4416== by 0x467A32: doFreeResource (resource.c:873) ==4416== by 0x467B7E: FreeResource (resource.c:903) ==4416== by 0x547742: ProcRenderFreePicture (render.c:661) ==4416== by 0x54B13A: ProcRenderDispatch (render.c:1988) ==4416== by 0x430670: Dispatch (dispatch.c:428) ==4416== by 0x492604: main (main.c:288) ==4416== Address 0x121031e0 is 116,960 bytes inside a block of size 122,880 free'd ==4416== at 0x4A079AE: free (vg_replace_malloc.c:427) ==4416== by 0x5D16BDA: qxl_surface_cache_evacuate_all (qxl_surface.c:1060) ==4416== by 0x5D13078: qxl_leave_vt (qxl_driver.c:1209) ==4416== by 0x4A4D4F: xf86VTSwitch (xf86Events.c:462) ==4416== by 0x4A4926: xf86Wakeup (xf86Events.c:285) ==4416== by 0x43E2E1: WakeupHandler (dixutils.c:421) ==4416== by 0x488A75: WaitForSomething (WaitFor.c:224) ==4416== by 0x4303CF: Dispatch (dispatch.c:357) ==4416== by 0x492604: main (main.c:288) This is fixed by a following patch to not free all_surfaces, instead keeping pointers from it to the evacuated list. commit fdf2274c9ff15fc5108b400b71120184d2651a21 Author: Alon Levy Date: Mon May 21 11:31:23 2012 +0300 qxl_surface: remove redundant qxl_garbage_collect, qxl_allocnf calls it commit b26640c2d24e6094487ea323a08b539d66050d89 Author: Alon Levy Date: Tue May 29 13:47:44 2012 +0300 uxa-damage: remove unnecessary include (doesn't fix any warnings) commit 8faf24226a20901e7a29019c922359365501ba69 Author: Alon Levy Date: Tue May 29 13:09:46 2012 +0300 uxa: remove unnecessary includes, reduces warnings due to duplicate definitions commit bb1b5865bc02341cfa1cc0d943fac59077643ea5 Author: Dave Airlie Date: Wed Jun 6 17:17:09 2012 +0100 qxl: fix spice build reported by jenkins. Signed-off-by: Dave Airlie commit f1a9c1b33bff038807755824190c69889ef6d794 Author: Dave Airlie Date: Wed Jun 6 14:19:04 2012 +0100 qxl: add API compat for latest X server. Signed-off-by: Dave Airlie commit 40dc75db615c1161c38874c145770b8d77a995ad Author: Yonit Halperin Date: Mon Apr 23 09:04:36 2012 +0300 Do not call update_area when lacking device memory The QXL_IO_NOTIFY_OOM is intended exactly for handling occurrences of lacking memory. The spice server tries to first release resources that are no longer in the current tree (and thus, do not need rendering). It renders drawables only as a last resort. And even then, it does not update the whole primary surface, but rather renders the oldest X drawables. The call to update_area is redundant, and its effect on performance is noticeable when playing full screen video. Signed-off-by: Yonit Halperin commit 1ad5c8633cc52aef5aa8a58bf529859caa8d1bd9 Author: Christophe Fergeau Date: Wed Apr 25 16:18:19 2012 +0200 Distribution-agnostic XORG_MACROS_VERSION check message commit b3caf8621d798ea3c5140b8adc193121c4c13c4a Author: Yaniv Kaul Date: Tue Apr 24 13:22:41 2012 +0300 Change xorg-macros -> xorg-x11-util-macros in configure error message At least in Fedora 17, the correct RPM name is xorg-x11-util-macros commit 315c7de54699a883ba91f906ab59985e4e5426c2 Author: Alon Levy Date: Mon Apr 9 12:27:21 2012 +0300 qxl_driver: ifdef out qxl_wait_for_io_command if not XSPICE commit 5eae282231a7cd727ad3f3576ed0574a29d949b1 Author: Alon Levy Date: Mon Apr 9 12:18:36 2012 +0300 qxl_surface.c: fix -Wshadow warning qxl_surface.c:735:6: warning: declaration of 'i' shadows a previous local [-Wshadow] commit a4773c508735ec3e9779c46e966a07d388e265dc Author: Alon Levy Date: Wed Apr 4 15:45:12 2012 +0300 mspace: no more warnings commit 01f77c0c42e2b9058e48a9ac4ae171609119ce12 Author: Alon Levy Date: Wed Apr 4 16:07:47 2012 +0300 spiceqxl_io_port: use pointer_to_u64 (fix warning) commit aa6cf3db29886ce964b9a5793e7ed0af6f213b57 Author: Alon Levy Date: Wed Apr 4 16:07:29 2012 +0300 spiceqxl_io_port: use attribute printf (fix warning) commit f7949ea1488223b8fcbfd1cba2d344d811ee62ca Author: Alon Levy Date: Tue Apr 24 12:11:42 2012 +0300 qxl_surface: qxl_surface_prepare_access: remove assigned but unused variables commit ae50a549d6169d806105a2d6b8220002fda5d9de Author: Alon Levy Date: Fri Apr 6 10:30:12 2012 +0300 spiceqxl: Xspice is spelled with a lower s commit ea37df4b4bccd4e2b9dbd590607737b18ec11ee9 Author: Alon Levy Date: Sun Apr 15 11:53:51 2012 +0300 README.xspice: updpate repository Reported by: Michael Tokarev commit fe74d8a89534d92a2151ee3ac28de3d5864a874e Author: Alon Levy Date: Thu Mar 8 16:16:20 2012 +0200 protect AC_CHECK_FILE for cross compiling Signed-off-by: Alon Levy commit 810d92db84d0e11260ec3abc936dacac113fcbba Author: Dave Airlie Date: Sun Mar 18 20:47:41 2012 +0000 qxl: missed one file that needs config.h commit 34ccb90f6b635b45080e6e4868314fa80a662fc2 Author: Dave Airlie Date: Sun Mar 18 20:42:38 2012 +0000 qxl: fix config.h usage You have to include config.h at top of each C file, not inside a header file. Signed-off-by: Dave Airlie commit b75eed01fa7514c15f4379092a93ecf8478f0b48 Author: Søren Sandmann Pedersen Date: Thu Mar 15 13:49:52 2012 -0400 Version bump to 0.0.17 commit c358c7f199bfeb519e08b0903438e43b1afd02c1 Author: Søren Sandmann Pedersen Date: Thu Mar 15 13:49:42 2012 -0400 Add qxl_option_helper.h to Makefile.am commit 81bee3d3491ab6b31b0d69207729280e86138d50 Author: Søren Sandmann Pedersen Date: Thu Mar 15 13:42:04 2012 -0400 In qxl_prepare_access(), don't modify the width/height of the pixmap The width and height were not properly restored, which caused GetDrawableInfo() to return bogus results, which caused GNOME shell crashes. Signed-off-by: Soren Sandmann commit 773fbda754de2dd91f8b6bfe754d1aa59368072b Author: Søren Sandmann Pedersen Date: Wed Mar 14 12:38:03 2012 -0400 qxl_surface.c: Remove #if 0'd debug spew commit 4724bb7922e1bb193117f13ffbd69fa4f97a29fb Author: Søren Sandmann Pedersen Date: Fri Mar 9 12:09:17 2012 -0500 options: Turn surfaces and caching on by default commit babe13196137f339b6f55c6382f7bd1c11100ec2 Author: Alon Levy Date: Thu Feb 16 15:55:21 2012 +0200 missed when added qxl_option_helpers.c commit 70d0d49b7c7d115f297dae710b9bb62b97fa22d5 Author: Alon Levy Date: Sun Jan 22 19:26:11 2012 +0200 replace lookup3 with MurmurHash3 See http://code.google.com/p/smhasher/wiki/MurmurHash3 Performance quotes from there are 2.5 times what lookup3 can do, for 32 bit variant, which is what we use: Lookup3_x86_32 - 1234 mb/sec Lookup3_x64_32 - 1265 mb/sec MurmurHash3_x86_32 - 3105 mb/sec New files are released to the public domain, keeping them that way. My own comparison shows the added hash to be ~45% faster then the existing one, see the tests at https://gitorious.org/hash_tests/hash_tests commit 994ac381a57e7a9ec502371c6aa3f491c1f1165f Author: Alon Levy Date: Fri Feb 3 15:26:44 2012 +0100 xspice_keyboard_proc: fix arrow keys Not sure yet why the regression with the arrow keys not producing the right keysym, but it appears that the keycode is correct for up (as an example), 111, but the keysym being produced was 0xff6c and not the correct 0xff52. This boiled down to the rules of the default rmlvo being "base" instead of "evdev". Providing an rmlvo parameter to InitKeyboardDeviceStruct allows to override that. The chosen rules = "evdev" model = "pc105" layout = "us" Is the default used by Xephyr from xorg-x11-server-Xephyr-1.11.99.901-3.20120124.fc17.x86_64 commit 8b3c5a5fac297226a467ea15c16cea8e5da51b8f Author: Alon Levy Date: Wed Feb 1 08:45:10 2012 +0200 introduce qxl_option_helpers.[ch] commit 96349ebb43e7de49b6b561b79d6fff5ada7aa4c7 Author: Søren Sandmann Date: Thu Feb 9 16:54:29 2012 -0500 Don't leak the surface when we run out of video memory. Running out of video memory would cause send_create_surface() to return NULL without putting the allocated surface back on the free list. Fix this by not allocating the surface until after the video memory is allocated, and, if surface allocation fails, freeing the video memory. commit c5ab0b538bd437e5e4aa86678d12a676c6edd1e8 Author: Søren Sandmann Date: Thu Feb 9 16:51:56 2012 -0500 Move check for zero width/height surfaces to qxl_surface_create() commit 1a371d76e32b2b3612e37ae15684632c987699b7 Author: Søren Sandmann Date: Tue Feb 7 14:36:05 2012 -0500 In qxl_check_copy() accept pixmaps that don't have surfaces UXA will correctly fall back to using PutImage if the pixmaps are not in offscreen memory (ie., they don't have surfaces attached), so there is no need to return FALSE in qxl_check_copy() just because the pixmaps don't have surfaces associated with them. commit 5a6715a074abb5ab2eb0513038a1ba8bd577239e Author: Alon Levy Date: Tue Jan 31 00:23:27 2012 +0200 xf86PciInfo.h is deprecated and unused, drop it commit d07c8acc3057cc577a67d04dc8499e2996ed30b4 Author: Alon Levy Date: Tue Jan 31 00:23:09 2012 +0200 qxl_image: cleanup qxl_image_create commit da1d595ad303b997343df91ebfb70c82c4bbb748 Author: Alon Levy Date: Mon Jan 30 23:17:43 2012 +0200 Enable surface and caching option defaults for Xspice commit df89dc60feda16de907e93bf7da381873c01644f Author: Alon Levy Date: Mon Jan 30 23:14:32 2012 +0200 xspice: remove duplicate declaration (fixes warning) commit d6eedbdfbbb2eee8e6ff119dc0c12ff173a1cef6 Author: Søren Sandmann Pedersen Date: Mon Jan 30 16:15:05 2012 -0500 Enable surface and caching options for XSpice too commit 4e68645a4d9a84d009b603d908e5b20a47db0724 Author: Alon Levy Date: Sat Jan 28 13:47:20 2012 +0200 examples/spiceqxl.xorg.conf.example: fix in vm usage. RHBZ #785373 workaround vmmouse segfault by disabling udev in spiceqxl.xorg.conf.example. We don't really need it anyway since we explicitly specify drivers for video keyboard and mouse. commit 8ab5b7e0faa12bf5aa1575c5fd11230284f68d71 Author: Søren Sandmann Pedersen Date: Tue Jan 17 11:35:04 2012 -0500 Enable caching of images based on the configuration options commit 57b5d7a1a20fb4f138131e644251d1a49ede94c8 Author: Søren Sandmann Pedersen Date: Mon Jan 16 11:11:52 2012 -0500 Return NULL from qxl_surface_create() when surfaces are disabled commit 6acad24a8df389e72c923e02fedd615e56dfe15b Author: Søren Sandmann Pedersen Date: Mon Jan 16 10:54:57 2012 -0500 Add support for parsing various options - EnableImageCache - EnableFallbackCache - EnableSurfaces EnableImageCache will enable the use of caching for PutImage requests. EnableFallbackCache will enable the use of caching for image commands that are the result of fallback rendering. commit 1b5a3f606a16fd704cdbd296b29f7dc89faf8471 Author: Søren Sandmann Pedersen Date: Thu Jan 19 14:56:30 2012 -0500 Use u64_to_pointer() instead of a cast to void * commit 8aad7d4d95cd095e8e11cabbfde10b5d3b755a72 Author: Søren Sandmann Pedersen Date: Thu Jan 19 08:18:58 2012 -0500 Track damage for PolyLine fallbacks. This is a substantial speedup for the Gimp. commit c2ae430e572a1c7c8cebb8174482ded743a0b7f2 Author: Alon Levy Date: Tue Jan 17 17:50:18 2012 +0200 qxl-driver: call vgaHWSetStdFuncs explicitly Previously it was called via vgaHWGetHWRec, since 1.11.99.901 it is no longer so. The relevant xserver commit: 4bd6579188e718654c35f95623fd4772f9e0ef06 vgahw: Don't default to standard (port space) access routines From: Adam Jackson commit 37230939c6ebebac1ee9ce0f3de66a9a22355ab0 Author: Alon Levy Date: Fri Jan 13 17:15:42 2012 +0200 build fixes: sched_yield and missing declaration From: Adam Jackson commit c02da3f529513fc42afce9185e41852b8ae1407a Author: Søren Sandmann Pedersen Date: Wed Jan 18 08:40:18 2012 -0500 Guard access to "pci" with #ifndef XPSICE commit 89f71d1be4a28e7bf0d60e3089b1f28202fb821f Author: Søren Sandmann Pedersen Date: Tue Jan 17 11:24:29 2012 -0500 Fix mis-merge qxl_reset() has to be defined before qxl_close_screen(). commit 9600e4a0b57693da451f3f5ca61637d0275c3836 Author: Søren Sandmann Pedersen Date: Wed Oct 5 12:27:27 2011 -0400 Reset non-primary device out of CloseScreen(). Otherwise, client windows will linger even after the server shuts down. Don't reset the primary device so that we can preserve the fonts etc. commit e2fad1c9afe55ee9909a3ec8f142d2611f88b3f7 Author: Søren Sandmann Pedersen Date: Sat Mar 19 12:47:52 2011 -0400 If qxl_pre_init() is called without a confScreen, just return FALSE. Otherwise, the driver crashes when called from Xorg -configure. commit 420876da9eafeece83e4719a469d2e1ce0b13478 Author: Søren Sandmann Pedersen Date: Tue Aug 9 05:16:56 2011 -0400 Transmit images in smaller chunks This makes use of the 'chunks' feature of the SPICE protocol to send images in chunks smaller than 512 * 512 bytes. This reduces the likelihood of running out of memory when big image are transmitted. commit 5da2a6e4999265b717a7fd18039a90edcd9ba941 Author: Alon Levy Date: Sun Dec 18 19:48:57 2011 +0200 configure.ac: support autoconf 2.63 AC_CHECK_FILE(cond,[not-empty],[]) in autoconf 2.63 produces an empty else that is illegal for bash, but forgoes the else when given a AC_CHECK_FILE(cond,[not-empty]). 2.68 produces correct output on both, so it's unaffected. commit 10d122e7bc9a78be17b130c27495564562bf0f93 Author: Søren Sandmann Pedersen Date: Mon Dec 19 02:09:37 2011 -0500 Revert "Use new 8BIT_A format for 8 bit pixmaps." This reverts commit 8ea466a2f408524a9fcc08ed0a17f3c935857afa. (This change was pushed accidentally) commit 635a5887c52382b481de1ecca463a3fbb7fd6aa3 Author: Søren Sandmann Date: Sat Dec 17 05:39:32 2011 -0500 Don't translate newly generated paccess region The region passed to uxa_prepare_access() is in screen coordinates, but the driver wants drawable coordinates. Hence we do a translation. However, when the passed region is NULL, we generate the region ourselves based on the full drawable extents. This region is already in drawable space so shouldn't be translated. commit 8ea466a2f408524a9fcc08ed0a17f3c935857afa Author: Søren Sandmann Date: Tue Dec 13 03:51:35 2011 -0500 Use new 8BIT_A format for 8 bit pixmaps. commit 30b4b72cdbdf9f0e92a8d1c4e01779f60f15a741 Author: Gerd Hoffmann Date: Thu Oct 6 17:06:10 2011 +0200 support _ASYNC io calls and interrupt handling (busy wait) rebased with Xspice changes. Signed-off-by: Alon Levy commit c77ba9f217093f946a4c6bf6edf9f34b24844d8d Author: Søren Sandmann Date: Fri Oct 28 12:56:30 2011 -0400 Translate the access region according to the drawable offset. The driver code expects to be given coordinates relative to the offscreen pixmap. commit 0d3a9a626402ef0cc52430fe4cb35d7b5da68536 Author: Alon Levy Date: Tue Aug 2 21:31:11 2011 +0300 rename xspice Xspice commit 38cedb7ad42f11fe451507d82922d4e94cdcf15d Author: Alon Levy Date: Mon Aug 1 19:17:48 2011 +0300 xspice: make --cgdb non magical, use XSPICE_ENABLE_GDB commit b89a0b11b1133bf2991580203867830747ad4de1 Author: Søren Sandmann Pedersen Date: Sun Aug 21 10:11:48 2011 -0400 Only save the VGA fonts for the primary device. Otherwise, if we try to save the VGA fonts when initializing a non-primary device, the saving will be routed to the primary one putting it into VGA mode, which then locks up. commit 4a040532492b212e05f5375994adcfdd171e0410 Author: Søren Sandmann Pedersen Date: Tue Jul 26 13:44:30 2011 -0400 Ignore devices classes when matching PCI devices A device_class of 0x00030000 means we will only match VGA compatible controllers, but when multiple devices are added to the VM, the additional ones will have subclass 0x8000 ("Display controller"). We need to be able to drive those too. commit 3b851a37a7030688fc8ee361167b3567e6623edf Author: Alon Levy Date: Sat Jul 23 20:31:11 2011 +0300 spiceqxl.xorg.conf.example: typo and order fixes commit c65af64529a7abd99bd175e1e49fb98f289fb55a Author: Alon Levy Date: Sat Jul 23 20:08:35 2011 +0300 README.xspice: use consistent and vnc default port commit f637c79f121348b02b0fdae7613675acda380815 Author: Alon Levy Date: Sat Jul 23 20:07:04 2011 +0300 xspice: add missing --tls-port default commit dd90fa9277283b4806aba307ca952bea0402b534 Author: Alon Levy Date: Sat Jul 23 12:55:42 2011 +0300 0.0.16 Brown paper bag release: fix scripts/xspice to run. Did a few more fixes in the same time: less verbose error messages by default add missing command line in xspice for --x509-cert-file check for existance of certificates before running Xorg commit 7c4804541084ba8f1d11c4c86f0276235520c7be Author: Alon Levy Date: Sat Jul 23 13:36:10 2011 +0300 scripts/xspice: prevent running with missing certificates since spice-server aborts if it is missing any of the ca-cert, server cert or server key, when running with --tls-port, check for them ourselves first. also add missing --x509-cert-file switch, and add --disable-ticketing to example in --help. commit 432c5870104f583166234b9655628f359983bbfa Author: Alon Levy Date: Sat Jul 23 13:11:54 2011 +0300 spiceqxl_io_port: make dprint silent and read XSPICE_IO_PORT_DEBUG_LEVEL environment variable. commit 345c0b9818d5624858cfeb6b9ddba92d4eb64a0d Author: Alon Levy Date: Sat Jul 23 13:11:19 2011 +0300 scripts/xspice: fix --cgdb commit a596303cb1f6bfcbb3ea9575faa8e984c52eb5a8 Author: Alon Levy Date: Sat Jul 23 12:55:10 2011 +0300 scripts/xspice: fix to run commit ec418d246d083cb5c2317d2f5f6a775de252a5b9 Author: Alon Levy Date: Fri Jul 22 03:53:20 2011 +0300 0.0.15: now with xspice! xspice is a new X server that is also a Spice server. See README.xspice for more information. + added configure options to accomodate an extra driver (spiceqxl_drv.so) built alongside existing qxl_drv.so + XSPICE define where the code could not be split easily (qxl_driver.c and qxl.h) - no functional changes to driver. + some small cleanups (still too many warnings) commit a84cc00d777ee2e078eae94f3f65f8174dba9bdd Author: Alon Levy Date: Fri Jul 22 04:33:40 2011 +0300 add missing copyright headers commit 51003b11a0ad3ecccdb07319398ca7fb43af5271 Author: Alon Levy Date: Fri Jul 22 02:25:46 2011 +0300 xspice: fix make distcheck commit fa1700964a3da4cba85f61f4f563d39d09cd4cd4 Author: Alon Levy Date: Fri Jul 22 01:20:58 2011 +0300 xspice: add most options existing in qemu Add (copy from ui/spice-core.c) most of the options from qemu for spice server. Notable exception is per channel port selection, so the only sure way to verify all channels use the secure port (or the normal one) is to not set the port (or not set the secure port). About options: All options are settable either from environment variables called XSPICE_SOMETHING or by a SpiceSomething parameter in the Device section of the xorg configuration file. The xspice wrapper added later makes use of those environment variables and gives a more standard command line parameter interface. commit e289df207b63064d10455fec6e406171711605d5 Author: Alon Levy Date: Fri Jul 22 01:26:57 2011 +0300 spiceqxl_io_port: s/init_qxl_ram/xspice_init_qxl_ram/ commit e36912352b34f711dedc007d9b07eff8427bf5d0 Author: Alon Levy Date: Fri Jul 22 01:25:21 2011 +0300 xspice/qxl_driver: fix implicitly defined function warning commit bd39af72b31c9855362161d9e08c0b2e788dc143 Author: Alon Levy Date: Sun Jun 26 18:39:41 2011 +0200 xspice: qxl_unmap_memory: stop worker and track worker status stop the worker thread and track the status, ignoring io requests (i.e. prevent red_dispatcher writes to red_worker) if red_worker is stopped. This fixes spice red_worker asserts from actions attepted after X has destroyed a screen. X runs in a loop destroying and recreating screens multiple times during the process lifetime, whenever the last X client has exited. Note that the spice server is kept up during a X screen restart. This is on purpose, to avoid a spice-client closing when doing a window manager restart. Cleanup of the spice-server on atexit - still to do. commit c4aff2b753e80e49f88e95ea02d1ff1de21a2391 Author: Alon Levy Date: Wed Apr 27 18:44:29 2011 +0300 xspice/qxl_ring: yield when ring is full commit ee0e638e119d54fb3f9eda6de18f8fb3884d8509 Author: Alon Levy Date: Wed Apr 27 18:22:28 2011 +0300 xspice: README, TODO, config, xspice launcher script xspice is placed under scripts, adding a new Makefile.am, only installed if --enable-xspice. spiceqxl.xorg.conf.example is placed under examples to be installed to share/doc if --enable-xspice. commit 4d04f2bb72bf8d7aff6f33d2dd77d8c5c0e77f83 Author: Alon Levy Date: Wed Apr 27 18:20:09 2011 +0300 xspice: add inputs (mouse and keyboard) uses xf86AddInputDriver, xf86PostButtonEvent, xf86PostMotionEvent and xf86PostKeyboardEvent reused xspice_get_spice_server to access the single spice server instance. commit c54fef726efbf6c2f00e27b85a46a47741af1786 Author: Alon Levy Date: Wed Apr 27 18:24:38 2011 +0300 xspice: implement ioport_write commit c48574980b1c0924e04a93631ea8fda09f6487da Author: Alon Levy Date: Wed Apr 27 18:18:57 2011 +0300 xspice: add init_qxl_ram commit 0848656de81df95a456c740d59b05ab51a71b425 Author: Alon Levy Date: Wed Apr 27 17:58:17 2011 +0300 xspice: add display interface commit 6c92a6e04e7bc9fb86bacc426ee63e810f462f61 Author: Alon Levy Date: Wed Apr 27 17:48:15 2011 +0300 xspice: init spice server, add main loop Initialize a SpiceServer instance, and implement SpiceCoreInterface, that is fd read, write notification and watchs (timers). The SpiceServer instance creation is wrapped in xspice_get_spice_server to allow access from the pointer and keyboard drivers introduced later. The fd implementation is off because Xserver doesn't allow us to be notified on write unblock, only read. Workaround is to poll. commit 2e869f6489113a07300c937e6103a5e019b68f93 Author: Alon Levy Date: Wed Apr 27 17:45:31 2011 +0300 xspice: add SpicePort config option commit 3b99e0e25337aa8aecdc7b414ca8021cb12628e1 Author: Alon Levy Date: Wed Apr 27 17:25:42 2011 +0300 xspice: implement map_helper, unmap_helper, add init_qxl_rom Memory is taken from malloc instead of from the pci bar. Adds shadow_rom to qxl_screen_t. Introduces init_qxl_rom, which is directly taken from the qxl device in qemu. Plenty of TODO's added in this commit about various constants and about factoring out the code to not do this copy paste from qemu. commit 267ce8421450bdbe99df8ac0c0b55482a19203ef Author: Alon Levy Date: Wed Apr 27 17:14:27 2011 +0300 xspice: use spiceqxlModuleData as entry point commit 76fd7158eb521eb12a18835d7c4f1d3b5f156796 Author: Alon Levy Date: Wed Apr 27 17:10:57 2011 +0300 xspice: don't load ramdac and vgahw sub modules commit 00cc49ce4d8ce48b1c29e9a34c10db410483d4b2 Author: Alon Levy Date: Wed Apr 27 17:10:45 2011 +0300 xspice: allocate a single slot commit f3ca425f08275de670c963047a3d86e1fe481b2b Author: Alon Levy Date: Wed Apr 27 17:01:38 2011 +0300 xspice: stop using pci and vga This ifdefs out all the parts in qxl_driver.c that are pci device specific and are not required by xspice. No functional change. The resulting spiceqxl_drv.so doesn't run. commit 9e30b11be0134713922555a710b6be0e664b3af2 Author: Alon Levy Date: Wed Apr 27 16:30:21 2011 +0300 xspice: add spiceqxl_io_port, empty implementation commit d7c3e279081c932f30231a060feb5df3460055b2 Author: Alon Levy Date: Wed Apr 27 16:11:09 2011 +0300 xspice: use spiceqxl as name of xspice driver Fixes a few places that have hardcoded "qxl" to use the already defined QXL_DRIVER_NAME. commit f9d4fc347ba7927d4a723c3226f215bdcaab126e Author: Alon Levy Date: Wed Apr 27 16:04:52 2011 +0300 xspice: add configure option and make target adds --enable-xspice[=no|yes|only], which builds an spiceqxl_drv.so target, and defines SUPPORT_XSPICE. Fails build if spice-server not found. If you set to only it will not build the qxl driver. This is useful for easier packaging by not creating unpackaged artifacts. commit e76334e08ca939f1963ce7ece0e60a3d12247467 Author: Alon Levy Date: Tue Jul 19 09:33:43 2011 +0300 configure.ac: show configure options commit a8e911bf3862a29c2ef9648699448c40f206401a Author: Alon Levy Date: Wed Apr 27 14:41:24 2011 +0300 qxl_driver: introduce helpers for memory map and unmap qxl_map_memory_helper and qxl_unmap_memory_helper introduced, here as a simple split, later different implementation if XSPICE defined will be added. Nicer then putting an ifdef inside the map/unmap functions. commit edf1a6596388b1e5b871cb1be4a1f7c6ba75e4c1 Author: Alon Levy Date: Wed Apr 27 14:29:46 2011 +0300 qxl_driver: add setup_slot helper used in qxl_reset. Later ifdefed with XSPICE, which only uses a single slot covering all memory (really it should just avoid calling the translation mechanism, but for now this is simple enough). commit fc87a47c7fb61c2a5c3c8563b6fd1a500541649a Author: Alon Levy Date: Wed Apr 27 14:34:51 2011 +0300 add config options (empty for now) commit 141154b6b5cf3abf7855477b72edc1618a0188e1 Author: Alon Levy Date: Wed Apr 27 14:22:09 2011 +0300 partially replace outb with ioport_write replace everywhere it is needed for xspice - places left out are ifdefed out later based on the same define mentioned in qxl.h, XSPICE. commit e59bb1a7ea16751c185a9ab33d93f7242118cb4c Author: Alon Levy Date: Wed Apr 27 14:13:30 2011 +0300 qxl_ring: keep qxl pointer Add a qxl_screen_t* member to qxl_ring to make generelizing outb to ioport_write easier in the next patch. This means we do an extra deref and addition for every outb, but it means the code becomes simpler for doing outb in qxl_drv.so and a function call for spiceqxl_drv.so commit 3c6b36fab37e3d5ab66d0080bd086eaff9778ec6 Author: Alon Levy Date: Fri Jul 22 03:43:01 2011 +0300 qxl: bump spice-protocol to 0.8.1 for RINGs size defines commit 698d12e3f9c4b2c25f5730308203eae3f031d6e0 Author: Alon Levy Date: Tue May 24 14:27:22 2011 +0300 log git commit if any during load commit 5af8fd656deba5e2ac3d9fab161ff7cc80a68ec5 Author: Alon Levy Date: Tue May 24 11:17:02 2011 +0300 configure.ac: remove unused DRIVER_NAME commit 2a206fb4c4fdc7bb09e0b1e6cd6c4111c9268095 Author: Alon Levy Date: Fri Jul 22 01:27:26 2011 +0300 qxl_surface: fix unused print_region warning introduced a define for enableing it, DEBUG_REGIONS. commit ce5507756e87a73bc6275ac275073932cd122583 Author: Søren Sandmann Pedersen Date: Fri Jul 15 11:00:51 2011 -0400 Revert "use spice-protocol defines for ring sizes" This reverts commit 9ee2aa1ccefe1f3fecaf481f38581c9b0f201ef4. Conflicts: src/qxl_driver.c commit 9b915fab9d29f66b825d7a46e158f026f10a41dc Author: Søren Sandmann Pedersen Date: Thu Jul 14 13:59:55 2011 -0400 0.0.14 commit 4d4b0d35ce4c321b84f5686be6f16a58ae3f1980 Author: Alon Levy Date: Wed Apr 27 16:46:14 2011 +0300 cleanup (add eol between system and non system includes) commit 8bce20872cf3ef5bed5b6b6ce26f508c8466cd1e Author: Alon Levy Date: Wed Apr 27 14:58:53 2011 +0300 qxl_driver: whitespace fixes removing whitespace in lines consisting only of it. most of those areas are touched later, but not all. commit 9f4e67c41f708164ddd979d99471217d2f721807 Author: Alon Levy Date: Wed Apr 27 14:44:21 2011 +0300 qxl_driver: fix three incompatible pointer passed warnings commit 66a30b8e44e0ec173fbadfd233b4b204e33fb9f5 Author: Alon Levy Date: Wed Apr 27 14:31:25 2011 +0300 qxl_driver: fix initialization from wrong pointer warning commit 9ee2aa1ccefe1f3fecaf481f38581c9b0f201ef4 Author: Alon Levy Date: Wed Apr 27 14:24:35 2011 +0300 use spice-protocol defines for ring sizes commit a3d3ab64d24b966179c0af5d23ceaf4bf9dcf9f1 Author: Alon Levy Date: Wed Apr 27 14:03:38 2011 +0300 qxl.h: move spice include right after system includes commit 6f57df2d498a3cc8a4c44ca026624a51c09ccca1 Author: Alon Levy Date: Wed Apr 27 13:54:28 2011 +0300 qxl_image.c: fix defined but unused warning commit f0428e6f6308c5eba060678f8e98bc759fd5cb39 Author: Alon Levy Date: Wed Apr 27 13:53:01 2011 +0300 qxl_mem.c: fix pointer arithmatic warning (-Wpointer-arith) commit 37b1d20a57d7a6b5a2efc622b628230a16be8e3e Author: Alon Levy Date: Wed Apr 27 13:12:53 2011 +0300 qxl.h: double include protection commit b8b8754421f98cc6edc7993e5b65b9228102bcd1 Author: Søren Sandmann Pedersen Date: Sun Mar 20 14:15:07 2011 -0400 Fix VT switching - Surfaces need to be evacuated before switching VT - The device must be reset - Framebuffer access must be turned off - Pixmaps created while switched away must be created in host memory. commit ae165af1ad3fed9d6443f5733afecfb3b577a1aa Author: Søren Sandmann Pedersen Date: Sat Mar 19 13:38:06 2011 -0400 Generate tighter damage for PolyFillRect https://bugzilla.redhat.com/show_bug.cgi?id=552000 commit ab8fd100430f7a142799960ce371b36f4c673cda Author: Søren Sandmann Pedersen Date: Sat Mar 19 15:19:13 2011 -0400 Tiled upload of images after software fallback. Instead of uploading the full software image in one go, upload it in 512x512 tiles to avoid having to find huge huge blocks of memory and potentially running out. commit 81e3d5c118bbe75688be61d7739a48452f1a9415 Author: Søren Sandmann Pedersen Date: Sat Mar 19 11:56:00 2011 -0400 Don't re-upload read-only surfaces after a software fallback. Surfaces that were used in a read-only way don't need to be uploaded again since they didn't change. This is a particularly important optimization for Composite since sources and masks there don't have tight damage tracking regions. commit 3657800a64af34d04a18bbf342a648ab4e28c294 Author: Alon Levy Date: Thu Feb 10 20:08:24 2011 +0200 s/qxl_ram_header/QXLRam/ commit b25d9a5c425aad88da704df6a7551d12b4f140f1 Author: Alon Levy Date: Thu Feb 10 15:55:25 2011 +0200 use structs from spice-protocol qxl_dev.h A bunch of renames and just plain removal (where the struct was never used): * s/qxl_cursor_cmd/QXLCursorCmd/ * s/qxl_mode/QXLMode/ * s/qxl_command/QXLCommand/ * remove qxl_command_ext * s/qxl_rect/QXLRect/ * s/qxl_release_info/QXLReleaseInfo/ * remove QXLReleaseInfo_ext (prev qxl_release_info_ext) * s/qxl_clip/QXLClip/ * s/qxl_point/QXLPoint/ * s/qxl_pattern/QXLPattern/ * s/qxl_point16/QXLPoint16/ * s/qxl_brush/QXLBrush/ * s/qxl_mask/QXLQMask/ * s/QXL_BRUSH/SPICE_BRUSH/ * s/QXL_IMAGE/SPICE_IMAGE/ * s/qxl_image_descriptor/QXLImageDescriptor/ * s/qxl_data_chunk/QXLDataChunk/ * s/qxl_bitmap_format/SpiceBitmapFmt/ * s/qxl_bitmap/QXLBitmap/ * s/qxl_image/QXLImage/ * s/qxl_fill/QXLFill/ * s/qxl_opaque/QXLOpaque/ * s/qxl_copy/QXLCopy/ * s/qxl_transparent/QXLTransparent/ * s/qxl_alpha_blend/QXLAlphaBlend/ * s/qxl_copy_bits/QXLCopyBits/ * s/qxl_blend/QXLBlend/ * s/qxl_rop3/QXLRop3/ * s/qxl_line_attr/QXLLineAttr/ * s/qxl_stroke/QXLStroke/ * s/qxl_text/QXLText/ * use QXLBlackness,QXLInvers (typo needs fixing later), QXLWhiteness * s/QXL_CLIP_TYPE/SPICE_CLIP_TYPE/ * s/qxl_compat_drawable/QXLCompatDrawable/ * s/qxl_drawable/QXLDrawable/ * s/qxl_surface_cmd/QXLSurfaceCmd/ * remove qxl_compat_update_cmd * remove qxl_update_cmd * remove QXL_CURSOR_DEVICE_DATA_SIZE * s/CURSOR_TYPE/SPICE_CURSOR_TYPE/ * s/qxl_cursor/QXLCursor/ * s/qxl_rom/QXLRom/ * remove QXL_SURF_TYPE_PRIMARY * s/qxl_surface_create/QXLSurfaceCreate/ commit 9fd9bf3676f343ef3e124874c45add88ce2a084f Author: Alon Levy Date: Thu Feb 10 15:47:54 2011 +0200 use spice-protocol qxl_dev.h enums commit e18ea01e672e08e46f63292a5cd5a55be8b8f434 Author: Alon Levy Date: Thu Feb 10 12:47:54 2011 +0200 build: add spice-protocol dep commit 688d7679cb7dbcd57ec07cbaee9b115efbd639c1 Author: Alon Levy Date: Thu Feb 10 16:04:14 2011 +0200 qxl_driver: remove unused variable (found by gcc 4.6.0) commit 260ee7001a4c0f3bf5e93e355b23ee8417c7c63a Author: Alon Levy Date: Thu Feb 10 16:03:52 2011 +0200 gcc-4.6.0 fix: cast unmatching function pointers commit 4bfbdbb821ebd8112ea58b026f50c3d6d813cc67 Author: Søren Sandmann Pedersen Date: Wed Jan 26 08:42:32 2011 -0500 Bump version number commit 2258af0d7e970e5bf3a0d3722cfbc42b4e8fac6d Author: Søren Sandmann Pedersen Date: Wed Jan 26 07:43:08 2011 -0500 Remove calls to SourceValidate() from uxa-damage.c These were kludges and are not necessary with new servers. Note that they were never necessary for the QXL driver because we generally don't use software cursors. commit 46a91d2db56dc92ea2f661fa4ea20842c915773e Author: Søren Sandmann Pedersen Date: Mon Jan 24 21:50:53 2011 -0500 Make it compile with and without dixLookupPrivate() changes. commit 7d82b5470fd61765934665294a4437f46c7795bf Author: Søren Sandmann Date: Tue Nov 16 11:40:29 2010 -0500 Only add images to the cache if they are 128x128 or bigger Keep track of how many live images there are, for debugging purposes. commit acb4654ccf0aea3b66d0ac9397a13e9a2c1e7178 Author: Søren Sandmann Date: Tue Nov 16 11:33:01 2010 -0500 Add debugging code commit d7a3be0f400a68dd128f8040cb775bf369f9f7d3 Author: Søren Sandmann Pedersen Date: Tue Nov 16 08:41:50 2010 -0500 More explicit life cycle management commit 72ef08efd801538e60fd3dc2d46b0278a8df4b50 Author: Søren Sandmann Pedersen Date: Sat Nov 13 16:30:36 2010 -0500 Add a surface cache commit 32d119db640e9fd93eb0fd4f1efad31779bb63a9 Author: Søren Sandmann Date: Thu Nov 11 08:38:37 2010 -0500 Don't unmap memory until after uxa_close_screen; allow surface creation without vtSema commit 24a3f9f978c906b0be6dc04c3527b9dce5b6161a Author: Søren Sandmann Date: Sun Nov 7 04:08:15 2010 -0500 uxa: In change_window_attributes() only read back if the backing pixmap changed This speeds up log in time considerably. commit ed41c3a610af0c5182e52d72d311d06581594a39 Author: Søren Sandmann Date: Mon Oct 25 17:36:51 2010 -0400 Comment out debugging spew commit a3adaf144c05ff6fc9647cfb95147218ad68b401 Author: Søren Sandmann Date: Mon Oct 25 17:21:17 2010 -0400 Migrate pixmaps out of video memory before mode setting After mode setting migrate them back in. commit ffcfa53e0d8456d283bf2861fc72f94e08b9b37b Author: Søren Sandmann Date: Sun Oct 24 20:28:11 2010 -0400 Add qxl_surface_evacuates/replace_all() methods. These functions will be used to migrate surfaces out of video memory when we have to reset the device. commit 11d8429b82215a676219ed6c5515f02b61052c49 Author: Søren Sandmann Date: Fri Oct 22 12:38:00 2010 -0400 Track live surfaces in a doubly linked list. This allows us to find the surfaces that have to be moved out of video memory at reset time. commit 52b4325f00e79909342658938f479c7e0930a37e Author: Søren Sandmann Date: Thu Oct 21 22:14:28 2010 -0400 Track the pixmap in the surface struct. This is in preparation for making surfaces survives resets and mode sets. To do that, we'll need to evacuate them out of video memory, and then copy them back in. If some surface can't be reallocated, we'll need to make sure it is removed from its corresponding surface. commit efd9f6a3e379ed02c3eec059e7f26f54a82649e1 Author: Søren Sandmann Date: Tue Oct 19 15:43:07 2010 -0400 Move the surface lists into qxl_screen_t That way things don't break horribly if you have multiple graphics cards commit 31d4f0ce6208c78db4782c0d39dbe29fb8e25168 Author: Søren Sandmann Date: Tue Oct 19 14:45:39 2010 -0400 Fix a bunch of warnings commit 95ddf806c92a84f9517634382ffeb0e198186231 Author: Hans de Goede Date: Fri Oct 15 16:35:48 2010 +0200 free qxl->uxe with regular free not xfree commit de9ce7c7b8ca7c6eff9ff93cd668fbd3b13f2c76 Author: Hans de Goede Date: Fri Oct 15 16:30:58 2010 +0200 Fix the driver crashing qemu on 32 bits When casting a 32bit pointer to a uint64 the following happens: ptr -> int32 -> int64 -> uint64, so if the address is above 0x80000000 which is quite normal for mapped io, the int32 -> int64 cast causes sign extension, not good! Also fix the printing of the memslots the memslot phys addresses are always 64 bit, so tell printf to always read 64 bits, otherwise we end up printing the higher 32 bits of the address as size on 32 bits. commit 4fd3791cd4a36df6125a13fc9332ed3c9249cd2d Author: Hans de Goede Date: Fri Oct 15 20:24:05 2010 +0200 Don't access the qxl device when our vt is not focussed Trying to do so causes the X-server to segfault as it has dropped iopl permission, which is, erm, undesirable :) commit 3863e55263c6eff0fa7fbc544780c1cef5df9f0c Author: Hans de Goede Date: Thu Oct 7 18:35:23 2010 +0200 Fix restoration of text mode font when leaving the vt (rhbz#633647) commit fde682311fed56eb4f71dd2c4625218d7f77ce1a Author: Hans de Goede Date: Thu Oct 7 12:03:24 2010 +0200 Change default virtual size to match the highest available resolution With the old default virtual size of 1024x768, using higher resolutions is not possible without an xorg.conf. Since the default now a days is to not have an xorg.conf, this is sort of unfortunate. This patch makes these higher resolutions available, while keeping the default resolution used when none is specified through xorg.conf at 1024x786, so that the spice client window won't be way too large for smaller screens by default. Note that when running inside a vm with a qxl device with a 8MB framebuffer the code, for automatically setting the virtualsize to the largest width and height seen in the resolution list, would lead to a too large virtual size. The 8MB list has both 1920x1080 and 1600x1200 resolution which lead to a virtualsize of 1920x1200 which does not fit, there is a special check for this situation, which maximizes the available width in this case. This means that for using 1600x1200 on an 8MB device an xorg.conf is still necessary. This change does come at the prize of using more memory, but that seems like a reasonable price to pay to give us parity wrt supported resolutions with the windows driver. Also this is a must have to allow the to be written Linux agent to change the guest resolution to match the client machines one when running in auto fullscreen mode. In the long run we should add support for resizing the fb on the fly to match the resolution. commit 9b3e355f37841a6c25ebf2452614d3618367dc9c Author: Hans de Goede Date: Thu Oct 7 12:01:55 2010 +0200 Make non default qxl modes available Currently unless an xorg.conf with modelines is used a lot of the qxl supported modes like 1920x1080 are not available, because the xorg default modelines set does not have modelines for them. This patch adds code to dynamically generate modelines for all modes in the qxl mode list, bringing the xorg driver up to par with the windows driver wrt supported resolutions. Note that an xorg.conf specifying a large enough virtual screen size is still needed for resolutions > 1024x768, but one no longer needs to add modelines in it. This is fixed in my next patch. This patch also adjusts a few (fake) clock limits to make all the modes reported by the qxl device when compiled with a 16MB framebuffer work. commit 50e4a57bbe7535d289116c93fe79d20a435fb1ef Author: Hans de Goede Date: Thu Oct 7 18:35:58 2010 +0200 Make virtual resolution match actual resolution on startup When setting a Virtual size in xorg.conf, currently there can be a difference between the actual output resolution (say 1600x1200) and the virtual resolution (for example 2048x1600). This causes parts of the desktop too be unreachable because the cursor is bound to the spicec window and thus one cannot pan over the virtual desktop. Moreover other parts of the driver don't seem to like this and I've seen several hangs / crashes which I think are related. commit 0737c92f266a72a7d0c892bead5a77795bc06e12 Author: Søren Sandmann Date: Fri Oct 15 11:49:28 2010 -0400 Add put_image() acceleration commit 59b8ac875d5f1170864b0dc0215b6c6f4b82ef14 Author: Søren Sandmann Date: Fri Oct 15 10:25:16 2010 -0400 Some memory management changes commit 05f6f7d362c3b4c3e01f121d0f47964a12e18db4 Author: Søren Sandmann Date: Fri Oct 15 07:39:53 2010 -0400 Split uxa_glyphs_init() into uxa_init_resources(). uxa_glyphs_init() eventually allocates a pixmap which has to be done after damage setup since otherwise _dixInitPrivates(PIXMAP_PRIVATE) will assert(). commit 3240754854ba9e45e9299cbaeb1fdb0abf9d83e3 Author: Søren Sandmann Date: Thu Oct 14 14:00:01 2010 -0400 Add dependency tracking for surfaces. The device needs dependent surfaces to be referenced in surfaces_dest[] and surfaces_rects[] of the drawable command. commit d12291d684ff1d46285ae5928638cd33c96b8477 Author: Søren Sandmann Date: Sat Oct 2 01:43:21 2010 -0400 Fix surface_init() This function needs to set free_surfaces to NULL because it can be called multiple times. If not, the same surface will end up in the free list more than once, which leads to double frees and other bugs. commit 0b9dc625f399dfa5af6484a772673487afdaaa09 Author: Søren Sandmann Date: Sat Oct 2 00:30:05 2010 -0400 Fix segmentation fault when no surfaces are available commit d0457b0e8c8595fbbe4c56dafb5c7daf722b4b23 Author: Søren Sandmann Date: Wed Sep 29 06:49:51 2010 -0400 Faster malloc() implemenetation commit 9e86930d0d7a282d9db3fd8088f07445951175fd Author: Søren Sandmann Date: Wed Sep 29 00:01:37 2010 -0400 Delete most debug spew commit 2fb7e9bbd8264a79b35eca716acef565ad66fda1 Author: Søren Sandmann Date: Tue Sep 28 09:26:34 2010 -0400 Initial (buggy) support for copy area from offscreens commit fded799c0e7bfdbbdf53b4609641a98130b2dff2 Author: Søren Sandmann Date: Mon Sep 27 06:55:50 2010 -0400 Enable solid fills for surfaces commit 01dc285614fcd353f689da808fda7997b169e1c8 Author: Søren Sandmann Date: Mon Sep 27 06:12:30 2010 -0400 Fix surface bugs commit 553a4963fe653ca948db556d684eafb1980a68a7 Author: Søren Sandmann Date: Mon Sep 27 03:19:38 2010 -0400 Set the correct size of pixmaps commit e1e5366f25012caa51e309f3c96ad0bd7543e0d9 Author: Søren Sandmann Date: Wed Sep 15 11:04:24 2010 -0400 Update to the new dixPrivate ABI. commit c91939f318c472a2bfbc9fdbf526647dada87159 Author: Søren Sandmann Date: Fri Jul 9 10:27:24 2010 -0400 Initial preparations for surfaces. - Surface command enums - Memory contexts for allocating video memory - Internal API for creating, drawing to, and destroying surfaces commit 38cf1ae55006125cf502199b9fb3a4f75f4b009f Author: Søren Sandmann Date: Tue Jun 22 00:26:46 2010 -0400 Tighten damage region for fill rect commit 8f05e9149da590f4374557e8a41e8b9e35fa007f Author: Søren Sandmann Date: Tue Jun 22 00:12:27 2010 -0400 Make the fetched regions much tighter for core text commit 1f9bf097d5e983afbc38b60dc44e9386c8e226e7 Author: Søren Sandmann Date: Mon Jun 21 23:38:21 2010 -0400 At finish_access() do the whole region commit 4d3c4b28fbb0e2b684c9ffe859d1a4abaf7c22b5 Author: Søren Sandmann Date: Mon Jun 21 23:17:37 2010 -0400 Disable acceleration; fix bug with forgotten multiplication with bpp commit 23e9d51df8b8ece10356da850944b1bedc5167de Author: Søren Sandmann Date: Fri Jun 11 12:48:37 2010 -0400 Add some debugging code. commit 6ec925515fdaf403d85534283eb981be6aaf05fc Author: Søren Sandmann Pedersen Date: Fri Jun 11 11:30:42 2010 -0400 Add a region argument to prepare access. Download individual boxes. commit a10cb2087168dd068ffea666ea4aab2d076f4c12 Author: Søren Sandmann Pedersen Date: Thu Jun 10 13:45:29 2010 -0400 Add stripped-down version of damage.c as uxa-damage.c. Don't use it yet commit ae35aad56becedc7ebc2f858283b70b5fc382569 Author: Søren Sandmann Date: Wed Jun 9 12:18:39 2010 -0400 Add solid fill commit cbc9f2bc2f05bb4f4589873b86776eb4a4bce65c Author: Søren Sandmann Date: Wed Jun 9 12:05:40 2010 -0400 Add support for copying commit 3169cd116ee7210589b6bb8718f401d2f68ab773 Author: Søren Sandmann Date: Wed Jun 9 11:28:26 2010 -0400 Use the correct strides. It's now slow, but working commit d3cf206b9bfb3c59e2dfc113a7843ea421189239 Author: Søren Sandmann Pedersen Date: Thu Jun 10 10:56:41 2010 -0400 Remove unused ops from qxl.h commit 356b3ab866f6e2f16cdee3c0a60d9236879fc4c2 Author: Søren Sandmann Date: Tue Jun 8 08:34:37 2010 -0400 Disable all the damage handling commit 1bc58f80e13d9246cd38ac36d5ef299c4b5b9d3b Author: Søren Sandmann Date: Tue Jun 8 08:26:18 2010 -0400 Add prepare/finish access commit a222ec8a729d9e333b7e0873efa592ba3fcb7177 Author: Søren Sandmann Date: Thu Jun 3 10:49:48 2010 -0400 Initial prepare/finish access commit f7244b049674d5907a945ec355c17f33576586d1 Author: Søren Sandmann Date: Wed Jun 2 11:06:07 2010 -0400 Do initial uxa setup commit 65d5ac5a0eecbf0ee3abdb6998ce5e615e05bb4e Author: Søren Sandmann Date: Wed Jun 2 10:13:56 2010 -0400 Add a copy of UXA, but don't use it yet commit e691e979b476f1ff9c6c93e12f6cea02dcb8c841 Author: Søren Sandmann Date: Tue May 25 06:58:57 2010 -0400 Make sure memory space is not bigger than the memory commit 25329392a76eafa9cd583d2fa18b83e678dac3be Author: Søren Sandmann Date: Tue May 25 05:21:57 2010 -0400 More updates; it now works with surface0 commit 7c9c77fc0cb5a87b513c8438c17e6a81f3efaeab Author: Søren Sandmann Date: Tue May 25 04:27:17 2010 -0400 Update header to latest layout commit 760f19767b8e140f73a4e9e68774c45effcd6eff Author: Søren Sandmann Date: Fri Apr 23 13:23:53 2010 -0400 Initial support for surface0 commit 0ce5e9a3f459685ebdd900bfe2f85e073565f4bc Author: Søren Sandmann Date: Wed Apr 21 10:51:50 2010 -0400 Update qxl.h to deal with surface0 commit 586669561b01e289f989911cd363810420539435 Author: Soren Sandmann Date: Mon Apr 19 09:31:08 2010 -0400 Set correct amount of video memory commit e04e850ed7d07fc12f941fd13a5fc6f32f7153e1 Author: Soren Sandmann Date: Thu Apr 15 04:47:14 2010 -0400 Add memslot support commit 480e5028a60fb2a3d349200518241eb8bb27013c Author: Soren Sandmann Date: Tue Apr 13 08:35:04 2010 -0400 Memslots support commit 6973d3b2f76685ce6c0d2a73091d571263331174 Author: Soren Sandmann Date: Tue Apr 13 04:54:59 2010 -0400 Add memslot arguments to virtual_ and physical_address commit 797c9ad548c194407945c18b62dd0d10897ec332 Author: Soren Sandmann Date: Tue Mar 23 11:55:32 2010 -0400 Add rom entries and port definitions for the memslots. commit ae902114c1d9ffc40f21806cdc4ae78679b255dd Author: Christopher Hames Halse Rogers Date: Tue Aug 10 11:55:39 2010 -0400 Fix build against video ABI 8 PaintWindowBackground and PaintWindowBorder haven't been used in a while and have been removed from the Screen struct in Xserver 1.9. Reviewed-by: Adam Jackson Signed-off-by: Christopher James Halse Rogers commit 4640accec00aeb98f48f7d7d655301bbee409db8 Author: Gaetan Nadon Date: Wed Jul 21 16:49:04 2010 -0400 config: add comments for main statements commit 7be72f7633a48951fc766920ca504da8fce77770 Author: Gaetan Nadon Date: Wed Jul 21 16:07:00 2010 -0400 config: replace deprecated use of AC_OUTPUT with AC_CONFIG_FILES Signed-off-by: Gaetan Nadon commit 6514166ef339b699f8b83919d55091aa2678ca46 Author: Gaetan Nadon Date: Wed Jul 21 14:05:22 2010 -0400 config: replace deprecated AM_CONFIG_HEADER with AC_CONFIG_HEADERS Signed-off-by: Gaetan Nadon commit 09d6bc9587e3cc96cf5c16f9fda8c30eeeb2cba5 Author: Gaetan Nadon Date: Tue Jul 20 20:24:42 2010 -0400 config: remove unrequired AC_HEADER_STDC Autoconf says: "This macro is obsolescent, as current systems have conforming header files. New programs need not use this macro". Signed-off-by: Gaetan Nadon commit b6e42f71622fd86a8f30368e49aac4c35b4a0f14 Author: Gaetan Nadon Date: Tue Jul 20 19:41:31 2010 -0400 config: remove AC_PROG_CC as it overrides AC_PROG_C_C99 XORG_STRICT_OPTION from XORG_DEFAULT_OPTIONS calls AC_PROG_C_C99. This sets gcc with -std=gnu99. If AC_PROG_CC macro is called afterwards, it resets CC to gcc. Signed-off-by: Gaetan Nadon commit e6a4f32b962b08f1294b4c5e1a5c8220e9822b90 Author: Gaetan Nadon Date: Sun Jun 13 09:55:45 2010 -0400 COPYING: update file with Copyright notices from source code. Signed-off-by: Gaetan Nadon commit 563f31ad081038ece8b5a5b525dd516fcd6ad0fd Author: Dave Airlie Date: Sat Mar 13 19:52:56 2010 +1000 qxl: remove asserts that make no sense anymore Not sure why nobody noticed these, once I dropped qxl into Fedora it asserted. Looks like they were missed when p->Private was changed to an index. Signed-off-by: Dave Airlie commit d9a142d60603920069f478890884f20cbd7886b3 Author: Gaetan Nadon Date: Mon Feb 8 20:08:52 2010 -0500 config: move compiler flags from configure.ac to Makefile.am CFLAGS is an automake defined variable that should not be set by the module. It should not be AC_SUBST either, it already is. Use AM_CFLAGS in Makefile.am. This will allow the user to override the flags as they will be in the right order. The -g option is already part of the default flags. Signed-off-by: Gaetan Nadon commit 269276497a4f452cc943afe0eb9072519621d36e Author: Gaetan Nadon Date: Mon Feb 8 19:07:22 2010 -0500 config: remove unrequired '-I$(top_srcdir)/src' The current dir is already included by default in the makefile top_builddir = .. DEFAULT_INCLUDES = -I. -I$(top_builddir) Signed-off-by: Gaetan Nadon commit 64a42c229af2b6a530a022e0dfff78649d63bb70 Author: Gaetan Nadon Date: Mon Feb 8 18:42:52 2010 -0500 config: remove unused INCLUDES='-I$(top_srcdir)/src' This statement is redundant and not used in the makefile Signed-off-by: Gaetan Nadon commit ae33c52060a59e68466c47020d158950347007f1 Author: Gaetan Nadon Date: Mon Feb 8 18:12:38 2010 -0500 config: remove unused variable XORG_INCS Signed-off-by: Gaetan Nadon commit aa91dc597c38ae411dfb226c67e3ccffd12513f7 Author: Søren Sandmann Pedersen Date: Sat Feb 6 18:11:09 2010 -0500 Version 0.0.12 commit d4012a71266af13de979e6f7eb0c21bc69d28311 Author: Søren Sandmann Pedersen Date: Sat Feb 6 13:12:35 2010 -0500 Go back to drawing with QXL_EFFECT_OPAQUE instead of BLEND This was accidentally changed in 73ef1f79a64595a8. commit da82bd2146efae355018a81da64585cb902c3f27 Author: Søren Sandmann Pedersen Date: Sat Feb 6 13:10:34 2010 -0500 Fixing 15/16 bit confusion accidentally broke 24 bits. commit 4f1a188f6b655bd7e46d73b0a65ef161057e6ba4 Author: Søren Sandmann Pedersen Date: Sat Feb 6 17:36:26 2010 -0500 Bump version number to 0.0.11 commit f68d83c3d9e0aecb844b4903d3d0d6c32bdcf5cd Author: Søren Sandmann Pedersen Date: Sat Feb 6 17:34:53 2010 -0500 Eliminate some warnings about casting between u64 and pointers. commit fb0ad81f8c84108ae7b20aaea2642fa07228df65 Author: Søren Sandmann Pedersen Date: Sat Feb 6 12:22:29 2010 -0500 Bump version number commit 821b185478d0962a51bd8d9320a911d720fd8ce5 Author: Søren Sandmann Pedersen Date: Sat Feb 6 12:20:52 2010 -0500 Only use QXL "16 bit" modes when the screen depth is 15. What QXL calls 16 bit is actually x1r5g5b5. commit 2dd4e71fc90b5065fea088b5c4f0aa51c58b2611 Author: Søren Sandmann Pedersen Date: Sat Feb 6 10:36:48 2010 -0500 Remove some unused variables commit 73ef1f79a64595a8547c4f4d4f9161673a438319 Author: Søren Sandmann Pedersen Date: Sat Feb 6 09:32:00 2010 -0500 Do the undamage handling in qxl_copy_n_to_n() Previously it would do it in both copy_window() and copy_area(). The problem with that, aside from duplication of a bit code, was that sometimes the copy operations would be empty, which meant that no damage was generated for the copy so that the pending damage region was actually corresponding to some earlier operation, and so discarding it caused rendering corruption. This change moves the damage handling into qxl_copy_n_to_n() and only does something when there is actually some copying going on. commit 33f36098c766d86cb6f292e2806510d00e2caab6 Author: Søren Sandmann Pedersen Date: Thu Jan 28 06:44:57 2010 -0500 Store modes pointer out of qxl_map_memory() Since we have to map everything, then unmap it, then map it again, we can't store pointers to modes in the mode Private because the mapping may have changed. So, instead store an index, and make sure the modes pointer is stored map time and removed at unmap time. commit 443e5015d9c72022b3d5404c8381c7e6354ae799 Author: Alan Coopersmith Date: Wed Jan 27 20:29:46 2010 -0800 Fix build on OpenSolaris - Don't hardcode -Wall in Makefile.am since configure.ac sets it already - Use instead of Windows headers when building on Solaris, even which not using gcc Signed-off-by: Alan Coopersmith commit 57aaaa7e27334e167e4b3440773e6f5694b73568 Author: Dave Airlie Date: Wed Jan 27 14:49:08 2010 +1000 bump to 0.0.9 so you can't autogen on RHEL5 but you can build from tarballs no problem. commit fd0ac1f42cde8bcf28736ede176b0c9e36e52e88 Author: Gaetan Nadon Date: Sat Jan 23 11:14:08 2010 -0500 config: add INSTALL and ChangeLog #23814 All modules are required to have an INSTALL file and a ChangeLog file. Signed-off-by: Gaetan Nadon Signed-off-by: Dave Airlie commit 7295e82a55991ba1711fb6ebc48d57a1e1ffdbda Author: Gaetan Nadon Date: Sat Jan 23 11:14:06 2010 -0500 configure.ac: remove -I$(prefix)/include from INCLUDES #24676 Using $prefix for any purpose will yield incorrect results as not all modules uses the same prefix and that modules can use different directory which is configurable. The main include dir comes from XORG_CFLAGS Reported-By: Michael Olbrich Signed-off-by: Gaetan Nadon Signed-off-by: Dave Airlie commit 6dc97a050e52cf4d4dea22c934cd83dcf1f18013 Author: Gaetan Nadon Date: Sat Jan 23 11:14:05 2010 -0500 configure.ac: sdkdir usage duplicates the sdk include dir The sdkdir variable provides a duplicate copy of the include/xorg directory. The statement is removed as this was it's only used. In the Makefile, there is now only one instance of the -I sdkdir The sdkdir is provided in XORG_CFLAGS. Signed-off-by: Gaetan Nadon Signed-off-by: Dave Airlie commit fbc0da08ac3619695df93bcd8c4f7409babc47f7 Author: Gaetan Nadon Date: Sat Jan 23 11:14:02 2010 -0500 configure.ac: deploy the new XORG_DEFAULT_OPTIONS #24242 This macro aggregate a number of existing macros that sets commmon X.Org components configuration options. It shields the configuration file from future changes. Signed-off-by: Gaetan Nadon Signed-off-by: Dave Airlie commit bea47d64fdbbf96902ed67bf69db0f8f3752960e Author: Gaetan Nadon Date: Sat Jan 23 11:14:07 2010 -0500 make: Automake 'foreign' option is specified in configure.ac. #24206 Remove from Makefile.am Signed-off-by: Gaetan Nadon Signed-off-by: Dave Airlie commit 3dd6bd7fc5b6b00ed95d357dfadf3a258de7b7b5 Author: Gaetan Nadon Date: Sat Jan 23 11:14:04 2010 -0500 git: .gitignore: use common defaults with custom section # 24239 Using common defaults will reduce errors and maintenance. Only the very small or inexistent custom section need periodic maintenance when the structure of the component changes. Do not edit defaults. Signed-off-by: Gaetan Nadon Signed-off-by: Dave Airlie commit a2991214c03ffee9294cfad476d9d1ba2459d02b Author: Gaetan Nadon Date: Sat Jan 23 11:14:03 2010 -0500 packaging: add COPYING and README http://wiki.x.org/wiki/NewModuleGuidelines Signed-off-by: Gaetan Nadon Signed-off-by: Dave Airlie commit 528aeaa8620bbc2cc268204520d5b3bb8ca88ba7 Author: Søren Sandmann Pedersen Date: Fri Jan 15 09:17:10 2010 -0500 Bump to 0.0.8 commit 8fb67eda1c2b7366109973e36fa6f46bd44c63ae Author: Søren Sandmann Pedersen Date: Fri Jan 15 09:15:33 2010 -0500 Use QXL_EFFECT_OPAQUE instead of QXL_EFFECT_BLEND This is required to make video detection work. commit e658fa5182e57f58485095bc54e53b785d5db75e Author: Søren Sandmann Pedersen Date: Fri Jan 15 08:56:04 2010 -0500 Bump to 0.0.7 commit 1db64677301a52581d07f19e4bb44e4aa5a03004 Author: Søren Sandmann Pedersen Date: Fri Jan 15 06:48:35 2010 -0500 For fills, only undamage the parts that are actually filled. Sometimes more operations than one are coalesced into one damage event. this means filling should only undamage the parts actually filled. commit d35d1f6718c80f2497e930fc887fcea9172d6a91 Author: Søren Sandmann Pedersen Date: Mon Jan 11 04:58:25 2010 -0500 qxl: Keep track of a separate pending_copy damage region. To improve performance and fix some bugs in conjunction with moving windows around, keep damage in a separate pending_copy region. Accelerated operations then delete this region, and whenever new damage appears, it is unioned onto the "to_be_sent" region, which is then submitted at BlockHandler time. commit 24e525d36b9feb4adff4db76e8838c28d0027dbe Author: Søren Sandmann Pedersen Date: Mon Jan 11 03:45:20 2010 -0500 Change camel case names to underscore names. commit a3f1b11f035051cf3e1452f81d47da80e48e17c0 Author: Søren Sandmann Pedersen Date: Mon Jan 11 03:36:45 2010 -0500 Keep track of bytes_per_pixel in the qxl struct. Eliminate assumptions in various places that the mode is always 32 bits. This almost, but not quite, makes it work with 16 bit modes. The reason it doesn't work is that the QXL_BITMAP_FMT_16BIT assumes a 555 visual, but fb assumes an 565 visual. But now at least it doesn't crash anymore. commit 335f16154dc4c61950c95e9bbcd70e28654c1edb Author: Dave Airlie Date: Mon Jan 11 09:59:16 2010 +1000 bump version to 0.0.6 for release for Fedora inclusion commit 60a38ab077046a5ba457a3a39ace2a780dbbd3d6 Author: Dave Airlie Date: Mon Jan 11 09:39:00 2010 +1000 qxl: just call the wrapped function instead of the fb layer directly. this make it build against newer X servers a lot easier commit 6c1454c6eb7172438d5802f116b1322a46108d16 Author: Dave Airlie Date: Mon Jan 11 09:38:38 2010 +1000 qxl: move Damage creation to CreateScreenResources times. since damage isn't setup at Screen Init time commit 985ef88e65e6269f5afbe71d5fa59e87219f74ad Author: Dave Airlie Date: Mon Jan 11 09:38:14 2010 +1000 qxl: drop EXA usage for now since we don't use it yet. commit d8c0485bc8172dc77e38bf91599629678ae43b89 Author: Dave Airlie Date: Fri Jan 8 14:50:22 2010 +1000 qxl: make build against later servers commit 08687fc520fd8136cbac63c669e13c2fb3e504e8 Author: Dave Airlie Date: Wed Dec 23 13:27:12 2009 +1000 qxl: make default be 1024x768 This change lets us validate a lot of modes and if the user doesn't supply a virtual line they'll get 1024x768, because we have no monitor, its hard to know what the best thing to do in this case. To get larger modes add a Virtual line to xorg.conf commit 92c535f4ea0c3dbe50ccca2c730e0ab06c3aba55 Author: Dave Airlie Date: Wed Dec 23 13:26:15 2009 +1000 qxl: add sanity check to make sure we aren't back in VGA mode. For some reason we still sometimes end up back in VGA mode, I suspect a kernel VGA access somewhere while under KD_GRAPHICS, on real HW it would ignore this, qxl seems to "unique" and not in a good way. commit f7ba4bae1372e1c6f29416c69ee68856630aad52 Author: Dave Airlie Date: Tue Dec 22 20:02:36 2009 +1000 qxl: fix access to qxl ring This really needs to be volatile as the hw will change it underneath us. Really we should be using proper MMIO accessors for this stuff not casting structs to mmio memory. commit c581bf5524a7574b29cb2e4313eafb9344fd6b14 Author: Dave Airlie Date: Tue Dec 22 20:01:14 2009 +1000 qxl: fix 2 trivial cosmetic issues 1. use correct values for unmap vid mem BAR sizes, removes warning in logs. 2. print values from ROM not uninitialised values from qxl struct. commit b544f48047a4d1f12cca5ec15b6d9900e75a8cd4 Author: Søren Sandmann Pedersen Date: Sat Nov 7 03:42:07 2009 -0500 Bump version commit 5cfac92284baf988fd51c31d70cb0ee6d9dc9cd0 Author: Søren Sandmann Pedersen Date: Sat Nov 7 03:41:42 2009 -0500 Avoid submitting commands when device is in VGA mode. When someone runs "init 3", the device will be switched into VGA mode with no notification to the driver. 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. So this patch checks whether the device is in VGA mode before submitting each command. This is racy because the switch to VGA could happen just after we check, but there doesn't seem to be a better way. (Apart from removing the assert() in the QXL device itself thereby making it robust against command submission in VGA mode. But the author of the device is opposed to that). commit 71b30ea06a553e60580c7fe81e354f83b7c85327 Author: Søren Sandmann Pedersen Date: Fri Nov 6 13:05:06 2009 -0500 Do not wait for idle before mode set commit 35427856c51a5fd85526ff813fe60c4cd4994fcb Author: Søren Sandmann Pedersen Date: Wed Jul 15 00:10:16 2009 -0400 Reset before setting mode commit d9792b1b222d6597421047f319b323df9264e8a8 Author: Søren Sandmann Pedersen Date: Wed Jul 1 15:33:39 2009 -0400 Eliminate a couple of warnings commit 4df2ccebb6614fe664b6b6cf7a5a0cac54572a7a Author: Søren Sandmann Pedersen Date: Wed Jul 1 15:30:03 2009 -0400 Add lookup3.h to Makefile.am commit 123c16f82884777a54a751994f7a54a8a6716094 Author: Søren Sandmann Pedersen Date: Tue Jun 30 21:31:52 2009 -0400 Bump version number and include stdint.h commit fe61bc9f103e98161e9817ebe385f93e203b157e Author: Søren Sandmann Pedersen Date: Wed Jul 1 15:19:31 2009 -0400 Comment out some debugging spam commit f9eefc07ee0761fc64059597692e065b6bf16994 Author: Søren Sandmann Pedersen Date: Tue Jun 30 21:30:02 2009 -0400 Update vendor ID commit b0ce9d296678b2897dcf0939abe962dbe4ca61e5 Author: Søren Sandmann Pedersen Date: Mon Apr 27 19:02:46 2009 -0400 Remove mmtest.c test program commit 88f1bb8f9a5c2a4d80407c6b6f518871125f6784 Author: Søren Sandmann Pedersen Date: Thu Apr 9 08:24:12 2009 -0400 Dump display tree on OOM commit 62bc612d4b193bae569dcf431c0c5edf35d37360 Author: Søren Sandmann Pedersen Date: Thu Apr 9 07:23:13 2009 -0400 Use a linked hash table implementations; use the Jenkins hash function commit f2a92deb51fcf7f637f07accf5dbdd91bc253fee Author: Søren Sandmann Pedersen Date: Wed Apr 8 08:37:16 2009 -0400 Move hash table back into qxl_image.c commit 79fc46437aa4e7bc764de4cde5b031ca6bc30ba1 Author: Søren Sandmann Pedersen Date: Wed Apr 8 07:06:19 2009 -0400 Beginning of new hash table implementation commit c0e5e3e476d6db748176bab0950cc2fa80182fc9 Author: Søren Sandmann Pedersen Date: Tue Mar 31 04:28:46 2009 -0400 Bump version commit 69ab0c56d3d6bd06d1afec6d7744baa784a514f8 Author: Søren Sandmann Pedersen Date: Tue Mar 31 04:28:02 2009 -0400 Initial naive image caching implementation. It attempts to cache all image puts without exception; the id is produced by hashing and copying every time. commit a82edc29579887be456122ff65218d10b514000e Author: Søren Sandmann Pedersen Date: Sun Mar 29 23:58:47 2009 -0400 Compute hash codes for images commit 3c6fe57d45da1ff7e21df030e321377678ffbcff Author: Søren Sandmann Pedersen Date: Sun Mar 29 22:43:27 2009 -0400 Formatting qxl.h, add qxl_image_destroy() commit 4b48daf466e2bacdb4cb84080ea8df9009612693 Author: Søren Sandmann Pedersen Date: Sun Mar 29 22:30:07 2009 -0400 TODO; beginning of image hashing; move virtual/phsyical functions to qxl.h commit 7be9348dca4fa55289439b045079a9e819bf00e7 Merge: 23b2863 cb237a7 Author: Søren Sandmann Pedersen Date: Thu Mar 19 13:49:13 2009 -0400 Merge branch 'master' of git+ssh://sandmann@shell.devel.redhat.com/home/boston/sandmann/public_git/xf86-video-qxl commit 23b2863efd566d6d90e39fad6801dc3abe8bdeef Author: Søren Sandmann Pedersen Date: Thu Mar 19 13:48:49 2009 -0400 Cast-fest to get rid of void pointer arithmetic commit 45b0a8bef5b791c07a965ba7e11c45196f949c86 Author: Søren Sandmann Pedersen Date: Thu Mar 19 13:43:05 2009 -0400 Add pointer arithmetic warnings back in commit cb237a7b873206f1e11c4bc71e0d57b2d4a7bfbc Author: Søren Sandmann Pedersen Date: Wed Mar 18 14:13:33 2009 -0400 TODO commit b2e069f0d5ab1026d48787e8a22c8fb07a5095a6 Author: Søren Sandmann Pedersen Date: Thu Mar 12 13:00:42 2009 -0400 TODO commit 0e642938e8e627738e0ec6a3b1814ede2b2c6d7b Author: Søren Sandmann Pedersen Date: Fri Mar 6 09:34:07 2009 -0500 Reenable hardware cursors commit f4eb6409e5bbe44fa393dd952a5f8329da141a76 Author: Søren Sandmann Pedersen Date: Fri Mar 6 09:03:52 2009 -0500 Copy bits in qxlCopyNtoN rather than relying on a separate fbCopyArea() to do it This ensures that miHandleExposures() which get called at the end of fbDoCopy(), will be called with the shadow framebuffer in a sane state. This fixes a number of issues with scrolling partially covered windows. commit f453a3ab875f7c7dcfab09a69cdea92461e340b5 Author: Søren Sandmann Pedersen Date: Fri Mar 6 08:24:57 2009 -0500 Fix qxl_sleep(); make CopyArea deal better with Paintwindow damage commit 9ddbfa28c34554385223112ae5020282d234297a Author: Søren Sandmann Pedersen Date: Wed Mar 4 14:19:01 2009 -0500 Add hardware cursor support commit 779fd58b7394fb28d263ea8401bc6f0607007ef1 Author: Søren Sandmann Pedersen Date: Tue Mar 3 06:32:29 2009 -0500 Remove #if 0'd code commit fcc0996aee800ef3b2bf0984785f478e1ecccc19 Author: Søren Sandmann Pedersen Date: Tue Mar 3 06:28:10 2009 -0500 Add cursor structs; delete some unused variables commit 5b1f956905f389356f6abee9206e45c344a2d1b9 Author: Søren Sandmann Pedersen Date: Fri Feb 27 12:52:38 2009 -0500 Bump version number commit fa766e67eb8415affc0f10aaa30e28f0e0198faf Author: Søren Sandmann Pedersen Date: Fri Feb 27 12:52:07 2009 -0500 Use window offsets rather than pixmap offsets, among other things commit 1085a8a4c42605f75f820232648285faa7f82523 Author: Søren Sandmann Pedersen Date: Fri Feb 27 06:28:21 2009 -0500 Debug spam commit 818cdad0fc52553e3c38d5a23e5b9275b70778e7 Author: Søren Sandmann Pedersen Date: Thu Feb 26 10:55:29 2009 -0500 Add copy acceleration; use the right fill colors commit 5fed65c51d848748327079122a4e1c875f471e66 Author: Søren Sandmann Pedersen Date: Thu Feb 26 08:20:05 2009 -0500 Get the offsets closer to right commit a40a14c1f2c56f7d9d014a5e310e976471ba8c20 Author: Søren Sandmann Pedersen Date: Thu Feb 26 07:10:35 2009 -0500 Accelerate window background painting commit eb57c4f1968d08bc10320cf37394ff1a520f992d Author: Søren Sandmann Pedersen Date: Thu Feb 26 06:54:28 2009 -0500 Wrap PolyFillRects commit 80d208741c84b41c51f035fc71dbb523c319413f Author: Søren Sandmann Pedersen Date: Thu Feb 26 05:34:19 2009 -0500 Wrap CreateGC commit 39607168413c9708ac1104fa5e2568139b12b7a4 Author: Søren Sandmann Pedersen Date: Thu Feb 26 04:28:00 2009 -0500 TODO commit cfaa1868ef4b5c0cb81718200fb8fc5b64b617c5 Author: Søren Sandmann Pedersen Date: Wed Feb 25 06:35:34 2009 -0500 Don't use shadow framebuffer; use our own damage tracker instead. commit 0cdd4f50b175c4747b25a22335c4092c9de11018 Author: Søren Sandmann Pedersen Date: Wed Feb 25 05:22:25 2009 -0500 TODO commit f7d76a2d16db28c980f74ad16079b018502dfa17 Author: Søren Sandmann Pedersen Date: Tue Feb 24 13:45:40 2009 -0500 Set version number to 0.0.1 commit 5fdb5ad5234db2ece8b14eadf677e171ba3f1c18 Author: Søren Sandmann Pedersen Date: Tue Feb 24 13:44:53 2009 -0500 Wait for the command ring to go idle before setting the mode Set the correct stride in the ModifyPixmapHeader call, and onlhy call qxl_mem_free_all() if the memory system is initialized. commit 2be423058eec1bdd32dd55e124d9d7034494aad9 Author: Søren Sandmann Pedersen Date: Tue Feb 24 10:18:08 2009 -0500 Squash compiler warnings As part of this, don't use -Wpointer-arith commit e7cecde277f7eeed75db96ec5219d4da3ea1caed Merge: 4d0f59c 92046f5 Author: Søren Sandmann Pedersen Date: Tue Feb 24 10:10:14 2009 -0500 Merge branch 'master' of git+ssh://sandmann@shell.devel.redhat.com/home/boston/sandmann/public_git/xf86-video-qxl Conflicts: src/qxl_driver.c commit 4d0f59c148db48dbbacedd78c73f24874535a6bf Author: Søren Sandmann Pedersen Date: Tue Feb 24 10:09:30 2009 -0500 Free all memory on mode switch commit 92046f5ebd946a08f5064f6aaa888e8d71215ee1 Author: Søren Sandmann Pedersen Date: Tue Feb 24 09:58:03 2009 -0500 Call ModifyPixmapHeader() when mode switching This seems to be the best way to make sure the screen pixmap has the right size. commit 2212716b046053084ad1d01daf8b623aec871cc0 Author: Søren Sandmann Pedersen Date: Tue Feb 17 05:45:29 2009 -0500 Working garbage collection commit 68f2a3083dedee88d30826ca27a53ec2135493f1 Author: Søren Sandmann Pedersen Date: Tue Feb 17 05:45:00 2009 -0500 TODO commit c10d91829975a07c87678dba1655eeacbab005ba Author: Søren Sandmann Pedersen Date: Thu Feb 5 16:20:03 2009 -0500 Initial garbage collection support commit 505d8113b15c39e1be8799cf79ede2d66ac44537 Author: Søren Sandmann Pedersen Date: Wed Feb 4 20:05:38 2009 -0500 Implement qxl_ring_pop() commit bbeef5031346c8adfffd739f91f3c04a5c088412 Author: Søren Sandmann Pedersen Date: Wed Feb 4 19:28:37 2009 -0500 Zero the palette commit ecfd62b99811e149b20be5bc993774f4e992c281 Author: Søren Sandmann Pedersen Date: Wed Feb 4 19:18:40 2009 -0500 TODO commit 1b55a16a9758afc3c3ed825cb6eeea63cc145e7b Author: Søren Sandmann Pedersen Date: Wed Feb 4 19:13:01 2009 -0500 TODO commit 9740b860071ad8a198179464c2c24efb4b9b7b9f Author: Søren Sandmann Pedersen Date: Wed Feb 4 18:58:05 2009 -0500 Working image submission commit fa8e43057d4ac9bab5cd99c57151abdaebcbfb97 Author: Søren Sandmann Pedersen Date: Wed Feb 4 16:14:52 2009 -0500 Add bitmap structs, plus beginning of image creation commit b3921383a78e1d834f6822323699b860c4f42cf3 Merge: f82e6f9 73f4125 Author: Søren Sandmann Pedersen Date: Tue Feb 3 17:21:04 2009 -0500 Merge commit 'ajax/hwcursor' commit f82e6f91bb26dd049a946577c123bf4303f3fe96 Author: Søren Sandmann Pedersen Date: Tue Feb 3 17:20:49 2009 -0500 Add image structs and beginning of copy command submission commit 73f4125fb53a6928313c1b6e1a98222fcbfb7adf Author: Adam Jackson Date: Tue Feb 3 16:12:13 2009 -0500 Begin hardware cursor support. commit 42768eb171d0d7235d64c9ab23ba256af0248a52 Author: Søren Sandmann Pedersen Date: Tue Feb 3 16:09:47 2009 -0500 Draw random colors instead of ugly raster ops commit 11bdbadc3b6873215883d9013b8e6a3e9dc7b7e9 Author: Søren Sandmann Pedersen Date: Tue Feb 3 15:20:50 2009 -0500 TODO commit 12e47b5fae4c6f0610527c91598147b48fc82834 Merge: 8c7d6e5 3349acf Author: Adam Jackson Date: Tue Feb 3 14:44:13 2009 -0500 Merge commit 'ssp/master' commit 363352c761a421de24d19b761373b2cc1eb19b85 Merge: 3349acf 8c7d6e5 Author: Søren Sandmann Pedersen Date: Tue Feb 3 13:58:19 2009 -0500 Merge branch 'master' of git+ssh://sandmann@shell.devel.redhat.com/home/boston/ajackson/public_git/xf86-video-qxl commit 3349acf2e7296e5fc721489ea4a88bb2feda5c26 Author: Søren Sandmann Pedersen Date: Tue Feb 3 13:55:11 2009 -0500 Don't read memory before it's mapped commit 8c7d6e56137046efbad5b353a25c2171da01898a Author: Adam Jackson Date: Tue Feb 3 13:24:58 2009 -0500 Remove dead comment commit 92fd0dfed2a4379db30d7a1d67891db254b8fd29 Author: Adam Jackson Date: Tue Feb 3 11:06:01 2009 -0500 Typo fix. commit 02ebf439eafd2cdef72835bf34337cc2759870a5 Author: Adam Jackson Date: Tue Feb 3 11:04:49 2009 -0500 Read timestamp from the ROM. commit c6fe700b950e828e8efef1e9639cd30081fd2d14 Author: Adam Jackson Date: Tue Feb 3 11:00:10 2009 -0500 TODO: VRAM BAR notes. commit 94b814a675a07d28e6c81a878656a64c6c0b3770 Author: Adam Jackson Date: Tue Feb 3 10:28:21 2009 -0500 Add the standard X warning flags. commit 7eb3820f1fa09e5d411de9e02ef6572cbc4809fe Author: Adam Jackson Date: Tue Feb 3 10:18:04 2009 -0500 Silence a warning. commit 74940d595a69f413aad26f5413a26500cd796b6f Author: Adam Jackson Date: Tue Feb 3 10:17:28 2009 -0500 Typo fix. commit 4c6cb08fbf0759cbda8bcb7bf723c18aa261dba1 Author: Søren Sandmann Pedersen Date: Mon Feb 2 17:51:07 2009 -0500 Get rid of some warnings; setup two other rings commit 3fb2c5b7fb15b4f1dc1611f98f2b5070abbfa3da Author: Søren Sandmann Pedersen Date: Sun Feb 1 16:57:58 2009 -0500 Remove more debug spam commit 916735fea435670dbd95ea46c7b0aba87ba63416 Author: Søren Sandmann Pedersen Date: Sun Feb 1 16:54:34 2009 -0500 TODO; various cleanups commit cd2bb5852677ea4c45185f22fc6542c3562acd23 Merge: 7d82898 981485e Author: Søren Sandmann Pedersen Date: Sat Jan 31 16:23:36 2009 -0500 Merge branch 'master' of git+ssh://sandmann@shell.devel.redhat.com/home/boston/sandmann/public_git/xf86-video-qxl commit 7d82898b0a1984c8c4ee6e6ba88cd5383b5ffa26 Author: Søren Sandmann Pedersen Date: Sat Jan 31 16:21:31 2009 -0500 Use new qxl_ring data type for command submission Also add forgotten qxl_ring.c file commit 981485e95c9c07169e827729409955ff609ed037 Author: Søren Sandmann Pedersen Date: Sat Jan 31 16:21:31 2009 -0500 Use new qxl_ring data type for command submission commit 245ba829035abaacd400f3141bf2b7480fc4839d Author: Søren Sandmann Pedersen Date: Sat Jan 31 16:11:44 2009 -0500 Add qxl_ring.c to deal with general command submission commit 9cc17de8187c793c180c900885f1e4fcb6bafcd6 Author: Søren Sandmann Pedersen Date: Sat Jan 31 15:40:36 2009 -0500 Get rid of qxl_mem.h header commit 0f55802c13207c51c10f357d73d68e9ed976f5d4 Author: Søren Sandmann Pedersen Date: Fri Jan 30 17:58:13 2009 -0500 Pack to multiple of 1; remove forced crash commit 14357f8823170846654b840ed2f98052ef4ea605 Author: Søren Sandmann Pedersen Date: Fri Jan 30 16:43:34 2009 -0500 release_info is a union, not a struct commit 569f0dd215b6bfccaff8bb5d4046f246d1d11e67 Author: Søren Sandmann Pedersen Date: Wed Jan 28 17:48:35 2009 -0500 Add debug spam, hack around bug in the X server shadow code commit 75c60beccabbdc10daffe47b3aeca4468c4c9508 Author: Søren Sandmann Pedersen Date: Wed Jan 28 15:07:55 2009 -0500 Keep track of physical address of io pages; submit random fill on shadow update commit 99aca0a6089d8da7872f135579fd560dbbf6aa6d Author: Søren Sandmann Pedersen Date: Wed Jan 28 14:21:56 2009 -0500 Add ring macros; add a TODO file; clean up warnings commit 41af66fef212638bddcdc1de396474b1ad418b63 Author: Søren Sandmann Pedersen Date: Tue Jan 27 15:18:50 2009 -0500 More command submission preparation commit 1bf5406bea90937be90ade7f454b170e87ef4bf8 Author: Søren Sandmann Pedersen Date: Tue Jan 27 14:42:55 2009 -0500 Various preparations for command submission - Add some more enums - Remove debug spam - Hook up the memory manager commit e42e99ab31b15676f829a16610f5fb99d8fa5a84 Author: Søren Sandmann Pedersen Date: Sat Jan 24 14:11:09 2009 -0500 Comment out spamming commit 027cfcb5fba899f4191f4d2b18646dd667b7ff96 Author: Søren Sandmann Pedersen Date: Sat Jan 24 14:04:45 2009 -0500 Hook up the free list correctly when a block is not split commit cfadf617c396ab822bb8f5ae5f83c551843f845f Author: Søren Sandmann Pedersen Date: Sat Jan 24 13:57:44 2009 -0500 Only split a free block if there remains enough space for struct block commit d9a05473dbd7c407f3bd343bcc0348735616d579 Author: Søren Sandmann Pedersen Date: Sat Jan 24 13:34:21 2009 -0500 Fix a couple of free list corruptions; add more spam commit 0f4f80e6972eda1ddfad3f5e78d97fc5e3580238 Author: Søren Sandmann Pedersen Date: Sat Jan 24 12:50:32 2009 -0500 Fix bug where the new block was not correctly attached to its successor commit 014ae41cfbfd3ec18f514ba4fc282a1f3f906895 Author: Søren Sandmann Pedersen Date: Sat Jan 24 12:36:09 2009 -0500 Add qxl_mem.c and mmtest to build system commit 9e845d2beb65e1d1d672ba9784418e4aff8051d2 Author: Søren Sandmann Pedersen Date: Sat Jan 24 12:22:05 2009 -0500 Add mmtest.c plus various debug spam commit 49761efda465b8c49e5c28bd0aea58baa107d5d3 Author: Søren Sandmann Pedersen Date: Sat Jan 24 10:16:19 2009 -0500 Add simple-minded initial memory manager implementation. commit aff78c5631118a0a44f618d226bc193dc7b5858c Author: Søren Sandmann Pedersen Date: Fri Jan 23 16:50:10 2009 -0500 Release ring is uin64_t commit 7b7e81446a5f3936e6c9f2066f40646610f961c0 Author: Søren Sandmann Pedersen Date: Fri Jan 23 16:20:48 2009 -0500 s/unsigned int/uint32_t/ commit 8af2c6ef2d9fd8f2133d718d99811ba140c573f6 Author: Søren Sandmann Pedersen Date: Fri Jan 23 14:54:07 2009 -0500 Set the size of Video RAM to size of draw area commit 6c2ea58fcd364630558c73d9acc07b82add6b278 Author: Søren Sandmann Pedersen Date: Fri Jan 23 12:48:56 2009 -0500 Cast rom to uint32_t * commit 6168830b5aabafc8059cb0e91eff22b4ad32a266 Author: Søren Sandmann Pedersen Date: Fri Jan 23 12:11:55 2009 -0500 Restore mode offset calculation commit fd2e5aac8f6a65a3ecf1a5a816006431fe3a1646 Author: Søren Sandmann Pedersen Date: Thu Jan 22 22:00:38 2009 -0500 Add struct qxl_rom Fix up the mappings so that it will find the ram header magic word. commit cecfdaecade3c4987fce6d7ae4aa39f212b5ae55 Author: Søren Sandmann Pedersen Date: Thu Jan 22 21:01:19 2009 -0500 Change names to match QXL device; clear up confusion of ram vs. vram commit be24f31678d99112ffbe25e1807240663a8ff591 Author: Søren Sandmann Pedersen Date: Thu Jan 22 20:07:43 2009 -0500 Fix sizes without libpciaccess (they are reported in number of bits to shift) Also add more spam. commit 6f066a21b40dfd024b053c1673bdb26443690d4b Author: Søren Sandmann Pedersen Date: Thu Jan 22 18:38:18 2009 -0500 Make it compile without libpciaccess Also remove generated config.h.in file. commit f137dfbb16666208336b51b7300e82aa7b7613c9 Author: Adam Jackson Date: Tue Jan 6 13:32:34 2009 -0500 Add qxl_drawable commit dd2487205f14a366cb649d80efc3fabafb26b845 Author: Adam Jackson Date: Tue Jan 6 13:18:20 2009 -0500 include micmap.h commit bee8e67aff756d3ba3fe1fe5b8eea7386db6b982 Author: Adam Jackson Date: Tue Jan 6 13:16:13 2009 -0500 Fix return in CreateScreenResources commit 8b6f8f9199d776fb1e9f5983a1ca86caf078efd8 Author: Adam Jackson Date: Tue Jan 6 13:15:31 2009 -0500 Don't run off the end of qxlSwitchMode without explicit return commit f76cdcae1b85fda711b7b5822a11f35516ddd7d3 Author: Adam Jackson Date: Tue Jan 6 13:13:17 2009 -0500 Address, not value. commit 6e5b8af5a54ebe618dab3385950104a9fd793750 Author: Adam Jackson Date: Mon Jan 5 20:03:33 2009 -0500 Add shadow buffer. commit 1d7e19bfc1ac2ca5e49a7c14e7af339c755e5be3 Author: Adam Jackson Date: Thu Nov 13 15:41:18 2008 -0500 Add RAM header detail commit 7eb6bf971bbea04d24b95474646699b0c1b2f569 Author: Adam Jackson Date: Thu Nov 13 14:48:57 2008 -0500 Minor mode debugging commit 584be64b834e2a66c6f3a1b767166d3bad554d83 Author: Adam Jackson Date: Thu Nov 13 14:47:55 2008 -0500 Take bpp into account in mode selection commit 11d8d3b9e95b1319cb4493cd8cfdf9d8af0900d2 Author: Adam Jackson Date: Thu Nov 13 14:31:11 2008 -0500 Draw area setup commit 7b09fada2d63b238168434ac0e7e38085cf06b43 Author: Adam Jackson Date: Thu Nov 13 14:05:12 2008 -0500 qxl io ports are bytes, not ints. commit dfd38ead63e9cdf26629022fb2280b3758215187 Author: Adam Jackson Date: Wed Nov 12 16:47:11 2008 -0500 Get outl defined commit f01ed224725f6e1fa88a767ff602ec085bf76859 Author: Adam Jackson Date: Wed Nov 12 16:39:55 2008 -0500 More mode setup commit b0edee203a6773feabd1ea5b164ce739a68d55ea Author: Adam Jackson Date: Wed Nov 12 16:26:43 2008 -0500 Start adding mode setup. commit 65c145a94fd09d087f5446194ed72cb311ed85b1 Author: Adam Jackson Date: Wed Nov 12 16:22:28 2008 -0500 Add I/O port definitions, stash I/O base in the screen. commit fc45ca8ed1407cd68102e5b4214f4a35016dd30c Author: Adam Jackson Date: Wed Nov 12 16:02:15 2008 -0500 Add mode validation. commit 5152af1ca6780e40f328f04e77978541521b4cdb Author: Adam Jackson Date: Wed Nov 12 15:56:16 2008 -0500 Stash pointer to the mode list for easy walking. commit ebd3ff70eeb0fc7965b62b72014c2882b669feb1 Author: Adam Jackson Date: Wed Nov 12 15:53:39 2008 -0500 qxl_mode structure commit e3c066268661f85c50bf169d008a5494aa0e8414 Author: Adam Jackson Date: Wed Nov 12 13:34:32 2008 -0500 Validate command RAM signature commit e1687cbb7ccf0a30de24693447e1344b63b3922b Author: Adam Jackson Date: Wed Nov 12 13:32:48 2008 -0500 Attempt to find command RAM magic commit 241b23ced8ae172e9af63c49e2911b5e15a49efb Author: Adam Jackson Date: Wed Nov 12 13:28:32 2008 -0500 Size VRAM correctly. commit 0ca5c444978ebe9df13d4c5f600449074c2996e8 Author: Adam Jackson Date: Wed Nov 12 13:19:46 2008 -0500 Attempt to fix mode parsing commit 26ea8a0a68923c43aa25867b0535642855048649 Author: Adam Jackson Date: Wed Nov 12 13:17:11 2008 -0500 Print mode list commit cd32f8a879b578645053b09a8f9022a548754f1e Author: Adam Jackson Date: Wed Nov 12 13:10:55 2008 -0500 More ROM decode commit 4e93894c28656ab851ab5d7dee88b59ca3369eea Author: Adam Jackson Date: Wed Nov 12 13:06:10 2008 -0500 Fix endianness of ROM check commit 00dd02687c79db44768f835dadae72e5aa1b0ebd Author: Adam Jackson Date: Wed Nov 12 13:05:16 2008 -0500 Catch bad ROM signature commit 75b203c665b860602746f12b8cee6fb53307e805 Author: Adam Jackson Date: Wed Nov 12 13:00:11 2008 -0500 Start decoding the ROM header commit a7ab0239a0ebaf068e9e9b6115508db478e2e174 Author: Adam Jackson Date: Tue Nov 11 17:55:27 2008 -0500 More silliness commit bf117afcfe26de1db15b1c3ff0d84fa79926199b Author: Adam Jackson Date: Tue Nov 11 17:52:52 2008 -0500 Try to fix probe commit c2c19525444fff12f4a7dab9088abf87638f78c1 Author: Adam Jackson Date: Tue Nov 11 17:28:44 2008 -0500 Memory mapping setup commit 6caf30591c0039228662020c1f0b03b1183d9948 Author: Adam Jackson Date: Tue Nov 11 16:58:01 2008 -0500 Rename, qxlUnmapMem -> qxlUnmapMemory commit cb4f86b87bb28c2b64051082702957950838848e Author: Adam Jackson Date: Thu Oct 30 16:33:21 2008 -0400 Initial skeleton driver xserver-xorg-video-qxl-0.1.1/configure.ac0000664000000000000000000001434312236475573015273 0ustar # 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.1], [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg], [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) # Obtain compiler/linker options for the driver dependencies PKG_CHECK_MODULES(XORG, [xorg-server >= 1.0.99.901] xproto fontsproto $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 PKG_CHECK_MODULES([SPICE], [spice-server >= 0.6.3], [ AC_SUBST(SPICE_CFLAGS) AC_SUBST(SPICE_LIBS) ], ) else enable_xspice=no fi AM_CONDITIONAL(BUILD_XSPICE, test "x$enable_xspice" = "xyes") AM_CONDITIONAL(BUILD_QXL, test "x$enable_qxl" = "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]) PKG_CHECK_MODULES(XEXT, [xextproto >= 7.0.99.1], HAVE_XEXTPROTO_71="yes"; AC_DEFINE(HAVE_XEXTPROTO_71, 1, [xextproto 7.1 available]), HAVE_XEXTPROTO_71="no") AM_CONDITIONAL(HAVE_XEXTPROTO_71, [ test "$HAVE_XEXTPROTO_71" = "yes" ]) # 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/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} " xserver-xorg-video-qxl-0.1.1/TODO0000664000000000000000000001462212233750620013457 0ustar - 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.1/scripts/0000775000000000000000000000000012233752225014454 5ustar xserver-xorg-video-qxl-0.1.1/scripts/Xspice0000775000000000000000000002423512233751142015640 0ustar #!/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 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 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', default='0', *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 options not parsed by Xspice get passed to Xorg as is.") parser.add_argument('--xorg', default=which('Xorg')) 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') parser.add_argument('--config', default='spiceqxl.xorg.conf') # 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='standard spice port') parser.add_argument('--exit-on-disconnect', action='store_true', help='Exit the X server when any client disconnects') parser.add_argument('--deferred-fps', type=int, help='If given, render to a buffer and send updates only this many times per second') parser.add_argument('--tls-port', type=int, help='spice tls port', default=0) add_boolean('--disable-ticketing', help="do not require a client password") add_boolean('--sasl', help="enable sasl") parser.add_argument('--x509-dir', help="x509 directory for tls", default='.') parser.add_argument('--cacert-file', help="ca certificate file for tls") parser.add_argument('--x509-cert-file', help="server certificate file for tls") parser.add_argument('--x509-key-file', help="server key file for tls") parser.add_argument('--x509-key-password', help="key file password for tls") parser.add_argument('--tls-ciphers') parser.add_argument('--dh-file') parser.add_argument('--password', help="set password required to connect to server") parser.add_argument('--image-compression', choices = ['off', 'auto_glz', 'auto_lz', 'quic', 'glz', 'lz'], default='auto_glz', help='auto_glz by default') parser.add_argument('--jpeg-wan-compression', choices=wan_compression_options, default='auto', help='auto by default') parser.add_argument('--zlib-glz-wan-compression', choices=wan_compression_options, default='auto', help='auto by default') # TODO - sound support parser.add_argument('--streaming-video', choices=['off', 'all', 'filter'], default='filter', help='filter by default') add_boolean('--ipv4-only') add_boolean('--ipv6-only') parser.add_argument('--vdagent', action='store_true', dest='vdagent_enabled', default=False, help='launch vdagent & vdagentd') parser.add_argument('--vdagent-virtio-path', default='/tmp/xspice-virtio', help='virtio socket path') parser.add_argument('--vdagent-uinput-path', default='/tmp/xspice-uinput', help='uinput socket path') parser.add_argument('--vdagentd-exec', default='spice-vdagentd') parser.add_argument('--vdagent-exec', default='spice-vdagent') parser.add_argument('--vdagent-no-launch', default=True, action='store_false', dest='vdagent_launch') parser.add_argument('--audio-fifo-dir', default='') #TODO #Option "SpiceAddr" "" #add_boolean('--agent-mouse') #Option "EnableImageCache" "True" #Option "EnableFallbackCache" "True" #Option "EnableSurfaces" "True" #Option "NumHeads" "4" #parser.add_argument('--playback-compression', choices=['0', '1'], default='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): if not os.path.exists(args.vdagent_exec) or not os.path.exists(args.vdagentd_exec): return False for f in [args.vdagent_exec, args.vdagentd_exec]: if Popen(args=[f, '-h'], stdout=PIPE).stdout.read().find('-S') == -1: return False return True if args.vdagent_enabled: 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("erorr: 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_processes = [] def cleanup(*args): for f in cleanup_files: if os.path.isfile(f): os.remove(f) for p in cleanup_processes: p.kill() for p in cleanup_processes: p.wait() 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: cf = tempfile.NamedTemporaryFile(prefix="Xspice-", delete=True) cleanup_files.append(cf.name + ".log") args.config = cf.name xorg_args = [ '-logfile', cf.name + ".log" ] + 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() 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', 'deferred_fps', 'exit_on_disconnect', 'vdagent_enabled', 'vdagent_virtio_path', 'vdagent_uinput_path'] for arg in var_args: if getattr(args, arg): # 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 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 # TODO /tmp/xspice-vdagent - replace with temporary file in temporary directory vdagentd_uds = '/tmp/xspice-vdagent' if args.vdagent_enabled: for f in [vdagentd_uds, args.vdagent_virtio_path, args.vdagent_uinput_path]: if os.path.exists(f): os.unlink(f) 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, '-x', '-S', vdagentd_uds, '-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', vdagentd_uds]) 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.1/scripts/Makefile.am0000664000000000000000000000224012233750620016503 0ustar # 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.1/COPYING0000664000000000000000000000211212233750620014011 0ustar Copyright 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.1/autogen.sh0000775000000000000000000000030312046675641014772 0ustar #! /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.1/tests/0000775000000000000000000000000012233751142014124 5ustar xserver-xorg-video-qxl-0.1.1/tests/xspice_audio_test_helper.py0000775000000000000000000000122212233751142021550 0ustar #!/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.1/tests/xspice_audio_test.py0000775000000000000000000000061412233751142020215 0ustar #!/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.1/tests/xspice_util.py0000775000000000000000000000366512233751142017043 0ustar #!/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', '8000', '--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) xserver-xorg-video-qxl-0.1.1/README.xspice0000664000000000000000000000767712233750620015155 0ustar 1. 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 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 grab xserver, xspice, xextproto and xkbcomp for src in git://anongit.freedesktop.org/xorg/proto/xextproto \ git://anongit.freedesktop.org/xorg/app/xkbcomp \ git://anongit.freedesktop.org/xorg/xserver \ git://anongit.freedesktop.org/xorg/lib/libxkbfile \ git://git.freedesktop.org/git/spice/spice-protocol git://anongit.freedesktop.org/xorg/driver/xf86-video-qxl; do git clone $src; done 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 undesireable for testing (and running actually). export PKG_CONFIG_PATH=${TEST}/lib/pkgconfig (cd xextproto; ./autogen.sh --prefix=$TEST --without-xmlto && make install) (cd xserver; ./autogen.sh --prefix=$TEST && make install) (cd xkbcomp; ./autogen.sh --prefix=$TEST && make install) (cd libxkbfile; ./autogen.sh --prefix=$TEST && make install) (cd spice-protocol; ./autogen.sh --prefix=$TEST --datadir=$TEST/lib && make install) (cd xspice; ./autogen.sh --prefix=$TEST && make install) mkdir -p $TEST/etc/X11 place the tested config below in $TEST/etc/X11/spiceqxl.xorg.conf. last bit is a little ugly (FIXME), copy over the xkb bits from the existing X11 installation: mkdir -p $TEST/share/X11 cp -R /usr/share/X11/xkb $TEST/share/X11 = 3. Running = $XORG is either your own built $TEST/bin/Xorg or just the default Xorg Run server with: export XSPICE_PORT=5900 $XORG -noreset -config spiceqxl.xorg.conf :3.0 Or equivalently: ./xspice --port 5900 :3.0 Run X clients as usual by setting DISPLAY=:3.0 Run a spice client: spicec -h localhost -p 5900