summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Wong <gtw@gnu.org>2009-08-27 23:08:09 -0600
committerGary Wong <gtw@gnu.org>2009-08-27 23:08:09 -0600
commit2b6c97bcb50a8cb9442f42e8c72152b4765b385f (patch)
treeec8dd54bf2a4d9cadd050ef84a36cc40861e2414
parentefce818c23cd62bf352a0bb9f5f6d397288bed88 (diff)
Add support for RENDER extension.
-rw-r--r--ChangeLog11
-rw-r--r--Makefile.am8
-rw-r--r--configure.ac71
-rw-r--r--decorate-core.c23
-rw-r--r--decorate-render.c697
-rw-r--r--decorate-render.h8
-rw-r--r--gwm.c149
-rw-r--r--gwm.h8
8 files changed, 932 insertions, 43 deletions
diff --git a/ChangeLog b/ChangeLog
index 4190484..2a0d156 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
2009-08-27 Gary Wong <gtw@gnu.org>
+ * decorate-render.c, decorate-render.h: New files. Implement
+ window decoration with RENDER extension (text only, so far).
+ * gwm.c (setup_display, shutdown_display): Set up and clean up
+ RENDER extension when available.
+ (show_error): Show RENDER errors.
+
+ * decorate-core.c (core_update_window): Issue SetClipRectangles
+ request.
+
+2009-08-27 Gary Wong <gtw@gnu.org>
+
* gwm.c (sync_with_callback): Perform the sequence comparison
with unsigned arithmetic, so that overflow semantics are
well-defined.
diff --git a/Makefile.am b/Makefile.am
index d3be78f..b67b80d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
bin_PROGRAMS = gwm
-AM_CFLAGS = @XCB_CFLAGS@
-gwm_LDADD = @XCB_LIBS@
+AM_CFLAGS = @PKG_CFLAGS@
+gwm_LDADD = @PKG_LIBS@
gwm_SOURCES = \
actions.c actions.h \
button.c button.h \
@@ -13,3 +13,7 @@ gwm_SOURCES = \
root.c root.h \
utf8.c utf8.h \
window-table.c window-table.h
+
+if RENDER
+gwm_SOURCES += decorate-render.c decorate-render.h
+endif
diff --git a/configure.ac b/configure.ac
index 389fc34..b37d62d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -8,18 +8,47 @@ if test -f config.options; then
. ./config.options
fi
-AC_DEFINE(_GNU_SOURCE,1,Enable GNU extensions on systems that have them.)
+gwm_packages=""
+
+# GWM_MANDATORY_PACKAGE(PKG-NAME)
+AC_DEFUN([GWM_MANDATORY_PACKAGE],[
+ AC_MSG_CHECKING([for $1])
+ PKG_CHECK_EXISTS([$1],
+ [AC_MSG_RESULT(yes)
+ gwm_packages="$gwm_packages $1"],
+ [AC_MSG_ERROR(no)])
+])
+
+# GWM_OPTIONAL_PACKAGE(PKG-NAME, [LABEL], [DEPENDENCIES])
+AC_DEFUN([GWM_OPTIONAL_PACKAGE],[
+ gwm_label=m4_ifval([$2], [$2], [$1])
+ gwm_name=`echo $ECHO_N $gwm_label | tr 'a-z' 'A-Z' | tr -c 'a-zA-Z0-9' _`
+ gwm_have_deps=yes
+ for gwm_i in [$3]; do
+ if eval test "x\$gwm_found_$gwm_i" != "xyes"; then gwm_have_deps=no; fi
+ done
+ if test $gwm_have_deps = yes; then
+ AC_MSG_CHECKING([for $gwm_label])
+ if eval test "x\$with_$gwm_label" = xno; then
+ AC_MSG_RESULT(disabled)
+ else
+ PKG_CHECK_EXISTS([$1],
+ [AC_MSG_RESULT(yes)
+ gwm_packages="$gwm_packages $1"
+ eval "gwm_found_$gwm_label=yes"
+ AC_DEFINE_UNQUOTED(USE_$gwm_name, 1)],
+ [AC_MSG_RESULT(no)])
+ fi
+ fi
+])
+
+AC_DEFINE(_GNU_SOURCE, 1, [Enable GNU extensions on systems that have them.])
# Checks for programs:
AC_PROG_CC
PKG_PROG_PKG_CONFIG
# Checks for libraries:
-# pkg-config packages which must be present:
-packages="xcb"
-# pkg-config xcb- packages which can optionally be used:
-optional_xcb_packages="composite damage render shape xfixes"
-
AC_ARG_WITH(composite,[ --with-composite use the X Composite extension.])
AC_ARG_WITH(damage, [ --with-damage use the X DAMAGE extension.])
AC_ARG_WITH(render, [ --with-render use the X RENDER extension.])
@@ -32,19 +61,16 @@ AH_TEMPLATE(USE_RENDER,[Use the X RENDER extension.])
AH_TEMPLATE(USE_SHAPE,[Use the X SHAPE extension.])
AH_TEMPLATE(USE_XFIXES,[Use the XFIXES extension.])
-for i in $optional_xcb_packages; do
- AC_MSG_CHECKING([for $i])
- if eval test "x\$with_$i" = xno; then
- AC_MSG_RESULT(disabled)
- else
- iname=`echo $i | tr 'a-z' 'A-Z'`
- PKG_CHECK_EXISTS( xcb-$i,
- [AC_MSG_RESULT(yes)
- packages="$packages xcb-$i"
- AC_DEFINE_UNQUOTED(USE_$iname, 1)],
- [AC_MSG_RESULT(no)])
- fi
-done
+GWM_MANDATORY_PACKAGE(xcb)
+GWM_OPTIONAL_PACKAGE(fontconfig)
+GWM_OPTIONAL_PACKAGE(freetype2)
+GWM_OPTIONAL_PACKAGE(xcb-composite, composite)
+GWM_OPTIONAL_PACKAGE(xcb-damage, damage)
+GWM_OPTIONAL_PACKAGE(xcb-render, render, fontconfig freetype2)
+GWM_OPTIONAL_PACKAGE(xcb-shape, shape)
+GWM_OPTIONAL_PACKAGE(xcb-xfixes, xfixes)
+
+AM_CONDITIONAL(RENDER, [test x$gwm_found_render = xyes])
AC_SEARCH_LIBS(iconv, iconv)
@@ -54,9 +80,10 @@ AC_CHECK_HEADERS(iconv.h mcheck.h poll.h)
# Checks for functions:
AC_CHECK_FUNCS(iconv mtrace ppoll)
-PKG_CHECK_MODULES(XCB, $packages)
+PKG_CFLAGS=`$PKG_CONFIG --cflags $gwm_packages`
+PKG_LIBS=`$PKG_CONFIG --libs $gwm_packages`
-AC_SUBST(XCB_CFLAGS)
-AC_SUBST(XCB_LIBS)
+AC_SUBST(PKG_CFLAGS)
+AC_SUBST(PKG_LIBS)
AC_OUTPUT
diff --git a/decorate-core.c b/decorate-core.c
index 6976537..77b7436 100644
--- a/decorate-core.c
+++ b/decorate-core.c
@@ -33,6 +33,11 @@
#include "decorate-core.h"
+#define FONT_SIZE 12
+#define STRING2(x) #x
+#define STRING(x) STRING2(x)
+#define FONT_SIZE_STRING STRING( FONT_SIZE )
+
static xcb_font_t font;
static xcb_gcontext_t *gcs;
@@ -124,18 +129,20 @@ static xcb_void_cookie_t core_text( xcb_drawable_t drawable, int screen,
extern void core_update_window( struct gwm_window *window ) {
- /* FIXME SetClipRectangles? */
if( !window->cleared )
xcb_clear_area( c, FALSE, window->w, window->update.x, window->update.y,
window->update.width, window->update.height );
+ xcb_set_clip_rectangles( c, XCB_CLIP_ORDERING_YX_BANDED,
+ gcs[ window->screen ], 0, 0, 1, &window->update );
+
if( window->type == WINDOW_FRAME ) {
char *name = window->u.frame.child->u.managed.name;
- /* FIXME Define constants for position. */
core_text( window->w, window->screen, window == focus_frame ?
- COL_TITLE_ACTIVE : COL_TITLE_INACTIVE, 18, 12,
- name ? name : "(Untitled)" );
+ COL_TITLE_ACTIVE : COL_TITLE_INACTIVE,
+ FRAME_BUTTON_SIZE + FRAME_BORDER_WIDTH * 3,
+ FONT_SIZE, name ? name : "(Untitled)" );
} else if( window->type == WINDOW_FEEDBACK ) {
char *p, text[ 32 ];
int width;
@@ -316,8 +323,8 @@ extern void decorate_core_init( void ) {
int i;
uint32_t n;
- const char *font_name = "-*-lucida-bold-r-normal-sans-12-"
- "*-*-*-p-*-iso10646-1", /* FIXME allow font selection */
+ const char *font_name = "-*-lucida-bold-r-normal-sans-" FONT_SIZE_STRING
+ "-*-*-*-p-*-iso10646-1", /* FIXME allow font selection */
*cursor_font_name = "cursor";
xcb_char2b_t c2;
union callback_param p;
@@ -335,7 +342,7 @@ extern void decorate_core_init( void ) {
16, /* CURSOR_B */
14 /* CURSOR_BR */
};
-
+
font = xcb_generate_id( c );
xcb_open_font( c, font, strlen( font_name ), font_name );
@@ -384,5 +391,7 @@ extern void decorate_core_init( void ) {
extern void decorate_core_done( void ) {
+#if DEBUG
free( gcs );
+#endif
}
diff --git a/decorate-render.c b/decorate-render.c
new file mode 100644
index 0000000..c7baef8
--- /dev/null
+++ b/decorate-render.c
@@ -0,0 +1,697 @@
+/*
+ * decorate-render.c
+ *
+ * Part of gwm, the Gratuitous Window Manager,
+ * by Gary Wong, <gtw@gnu.org>.
+ *
+ * Copyright (C) 2009 Gary Wong
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 3 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Id$
+ */
+
+#include <config.h>
+
+#include <assert.h>
+#include <fontconfig/fontconfig.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_CACHE_H
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <xcb/render.h>
+#include <xcb/xcb.h>
+
+#include "gwm.h"
+
+#include "decorate-render.h"
+
+/* FIXME make this configurable */
+#define FONT_FAMILY "sans"
+#define FONT_WEIGHT FC_WEIGHT_BOLD
+#define FONT_PIXEL_SIZE 12
+#define FONT_LOAD_TARGET FT_LOAD_TARGET_LIGHT
+
+static FT_Library ftl;
+static FT_Face ftf;
+
+static const uint16_t decoration_cols[ NUM_COLS ][ 3 ] = {
+ { 0x2222, 0x3333, 0xEEEE }, /* COL_FRAME_ACTIVE */
+ { 0xAAAA, 0xAAAA, 0xAAAA }, /* COL_FRAME_INACTIVE */
+ { 0x0000, 0x0000, 0x0000 }, /* COL_BORDER */
+ { 0xFFFF, 0x0000, 0x0000 }, /* COL_BUTTON_ACTIVE */
+ { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_BUTTON_INACTIVE */
+ { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_TITLE_ACTIVE */
+ { 0x3333, 0x3333, 0x3333 }, /* COL_TITLE_INACTIVE */
+ { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_FEEDBACK_BACK */
+ { 0x0000, 0x0000, 0x0000 } /* COL_FEEDBACK_FORE */
+};
+
+#define METRIC_CACHE_SIZE_BITS 14
+#define METRIC_CACHE_SIZE ( 1 << METRIC_CACHE_SIZE_BITS )
+#define METRIC_CACHE_ASSOC_BITS 2
+#define METRIC_CACHE_ASSOC ( 1 << METRIC_CACHE_ASSOC_BITS )
+
+#define GLYPH_CACHE_SIZE_BITS 11
+#define GLYPH_CACHE_SIZE ( 1 << GLYPH_CACHE_SIZE_BITS )
+#define GLYPH_CACHE_ASSOC_BITS 3
+#define GLYPH_CACHE_ASSOC ( 1 << GLYPH_CACHE_ASSOC_BITS )
+
+#if GLYPH_CACHE_SIZE_BITS + GLYPH_CACHE_ASSOC_BITS > 16
+#error "Glyph cache is too big for 16-bit indices."
+#endif
+
+static struct metric_cache_line {
+ uint32_t c; /* character code */
+ unsigned int time;
+ int x_off, y_off;
+} metric_cache[ METRIC_CACHE_SIZE ][ METRIC_CACHE_ASSOC ];
+
+static struct glyph_cache_line {
+ uint32_t c; /* character code */
+ unsigned int time;
+} glyph_cache[ GLYPH_CACHE_SIZE ][ GLYPH_CACHE_ASSOC ];
+
+static unsigned int current_time;
+
+static void query_metrics( uint32_t c, int *x_off, int *y_off ) {
+
+ int row = c & ( METRIC_CACHE_SIZE - 1 );
+ int i, max;
+
+ for( i = 0; i < METRIC_CACHE_ASSOC; i++ )
+ if( metric_cache[ row ][ i ].c == c ) {
+ /* Cache hit. */
+ metric_cache[ row ][ i ].time = current_time;
+ *x_off = metric_cache[ row ][ i ].x_off;
+ *y_off = metric_cache[ row ][ i ].y_off;
+ return;
+ }
+
+ /* Cache miss. */
+ if( FT_Load_Char( ftf, c, FONT_LOAD_TARGET ) ) {
+ /* Couldn't load metrics. Don't bother evicting anything. */
+ *x_off = *y_off = 0;
+ return;
+ }
+
+ /* Search for a line to evict. */
+ for( i = 0, max = 0; i < METRIC_CACHE_ASSOC; i++ )
+ if( current_time - metric_cache[ row ][ i ].time >
+ current_time - metric_cache[ row ][ max ].time )
+ max = i;
+
+ metric_cache[ row ][ max ].c = c;
+ metric_cache[ row ][ max ].time = current_time;
+ *x_off = metric_cache[ row ][ max ].x_off =
+ ( ftf->glyph->advance.x + 0x20 ) >> 6;
+ *y_off = metric_cache[ row ][ max ].y_off =
+ ( ftf->glyph->advance.y + 0x20 ) >> 6;
+}
+
+static int query_glyph( uint32_t c ) {
+
+ int row = c & ( GLYPH_CACHE_SIZE - 1 );
+ int i;
+
+ for( i = 0; i < GLYPH_CACHE_ASSOC; i++ )
+ if( glyph_cache[ row ][ i ].c == c ) {
+ /* Cache hit. */
+ glyph_cache[ row ][ i ].time = current_time;
+ return ( row << GLYPH_CACHE_ASSOC_BITS ) | i;
+ }
+
+ return -1;
+}
+
+static int replace_glyph( uint32_t c ) {
+
+ int row = c & ( GLYPH_CACHE_SIZE - 1 );
+ int i, max;
+
+ /* Search for a line to evict. */
+ for( i = 0, max = 0; i < METRIC_CACHE_ASSOC; i++ ) {
+ assert( glyph_cache[ row ][ i ].c != c );
+ if( current_time - glyph_cache[ row ][ i ].time >
+ current_time - glyph_cache[ row ][ max ].time )
+ max = i;
+ }
+
+ if( glyph_cache[ row ][ max ].time == current_time )
+ /* Cache line set is full, and nothing is old enough to evict. */
+ return -1;
+
+ glyph_cache[ row ][ max ].c = c;
+ glyph_cache[ row ][ max ].time = current_time;
+
+ return ( row << GLYPH_CACHE_ASSOC_BITS ) | max;
+}
+
+static xcb_render_glyphset_t glyphset;
+static struct picture {
+ xcb_render_picture_t pic;
+ enum decoration_col col;
+} *pictures; /* indexed by screen */
+
+static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen,
+ int col, int x, int y,
+ const char *text,
+ const xcb_rectangle_t *clip ) {
+
+ xcb_render_picture_t src, dest;
+ xcb_rectangle_t rect;
+ xcb_render_color_t rc;
+ int i, len;
+ const unsigned char *p;
+ uint32_t *buf;
+
+ if( !text || !*text ) {
+ xcb_void_cookie_t r = { 0 };
+
+ return r;
+ }
+
+ len = 0;
+ p = (const unsigned char *) text;
+ for(;;)
+ if( !p[ 0 ] )
+ /* End of string. */
+ break;
+ else if( !( p[ 0 ] & 0x80 ) ) {
+ /* Legal single byte character. */
+ len++;
+ p++;
+ } else if( *p >= 0xC2 && *p <= 0xDF &&
+ p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF ) {
+ /* Legal two byte character. */
+ len++;
+ p += 2;
+ } else if( *p >= 0xE0 && *p <= 0xEF &&
+ p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF &&
+ p[ 2 ] >= 0x80 && p[ 2 ] <= 0xBF &&
+ ( *p > 0xE0 || p[ 1 ] > 0x80 ) ) {
+ /* Legal three byte character. */
+ len++;
+ p += 3;
+ } else if( *p >= 0xF0 && *p <= 0xF4 &&
+ p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF &&
+ p[ 2 ] >= 0x80 && p[ 2 ] <= 0xBF &&
+ p[ 3 ] >= 0x80 && p[ 3 ] <= 0xBF &&
+ ( *p > 0xF0 || p[ 1 ] > 0x80 ) ) {
+ /* Legal four byte character. */
+ len++;
+ p += 4;
+ } else
+ /* Illegal character: ignore this byte and continue. */
+ p++;
+
+ if( !len ) {
+ xcb_void_cookie_t r = { 0 };
+
+ return r;
+ }
+
+ buf = alloca( len * sizeof *buf );
+
+ for( p = (const unsigned char *) text, i = 0; i < len; i++ ) {
+ retry:
+ assert( *p );
+
+ if( !( p[ 0 ] & 0x80 ) )
+ /* One byte character. */
+ buf[ i ] = *p++;
+ else if( *p >= 0xC2 && *p <= 0xDF &&
+ p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF ) {
+ /* Two byte character. */
+ buf[ i ] = ( ( p[ 0 ] & 0x1F ) << 6 ) | ( p[ 1 ] & 0x3F );
+ p += 2;
+ } else if( *p >= 0xE0 && *p <= 0xEF &&
+ p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF &&
+ p[ 2 ] >= 0x80 && p[ 2 ] <= 0xBF &&
+ ( *p > 0xE0 || p[ 1 ] > 0x80 ) ) {
+ /* Three byte character. */
+ buf[ i ] = ( ( p[ 0 ] & 0x0F ) << 12 ) |
+ ( ( p[ 1 ] & 0x3F ) << 6 ) | ( p[ 2 ] & 0x3F );
+ p += 3;
+ } else if( *p >= 0xF0 && *p <= 0xF4 &&
+ p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF &&
+ p[ 2 ] >= 0x80 && p[ 2 ] <= 0xBF &&
+ p[ 3 ] >= 0x80 && p[ 3 ] <= 0xBF &&
+ ( *p > 0xF0 || p[ 1 ] > 0x80 ) ) {
+ /* Four byte character. */
+ buf[ i ] = ( ( p[ 0 ] & 0x07 ) << 18 ) |
+ ( ( p[ 1 ] & 0x3F ) << 12 ) | ( ( p[ 2 ] & 0x3F ) << 6 ) |
+ ( p[ 3 ] & 0x3F );
+ p += 4;
+ } else {
+ p++;
+ goto retry;
+ }
+ }
+
+ src = pictures[ screen ].pic;
+
+ dest = xcb_generate_id( c );
+ xcb_render_create_picture( c, dest, drawable,
+ gwm_screens[ screen ].root_pictformat, 0, NULL );
+
+ if( clip )
+ xcb_render_set_picture_clip_rectangles( c, dest, 0, 0, 1, clip );
+
+ if( pictures[ screen ].col != col ) {
+ rc.red = decoration_cols[ col ][ 0 ];
+ rc.green = decoration_cols[ col ][ 1 ];
+ rc.blue = decoration_cols[ col ][ 2 ];
+ rc.alpha = 0xFFFF;
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = 1;
+ rect.height = 1;
+ xcb_render_fill_rectangles( c, XCB_RENDER_PICT_OP_SRC, src, rc, 1,
+ &rect );
+
+ pictures[ screen ].col = col;
+ }
+
+ i = 0;
+ while( i < len ) {
+ uint32_t glyphids[ 0x80 ];
+ xcb_render_glyphinfo_t glyphs[ 0x80 ];
+ int num_glyphs; /* number of glyphs in AddGlyphs request */
+ /* Buffer for "data" parameter of AddGlyphs request. We want
+ this to be big enough to hold at least one glyph, but no
+ bigger than the biggest request the server will accept (once
+ the other parameters are included). All servers must
+ accept requests of 16,384 bytes or smaller (see X Window
+ System Protocol, section 8), so this is safe. */
+ uint8_t data[ 0x2000 ], *p;
+ /* Buffer for "glyphcmds" parameter of CompositeGlyphs16
+ request. We always send exactly one command, which has an
+ 8-byte header and at most a 254 glyph string. */
+ uint8_t param[ 8 + ( 0xFE << 1 ) ], *out;
+ int num_chars; /* number of characters for CompositeGlyphs16 */
+
+ current_time++;
+
+ p = data;
+ num_glyphs = 0;
+
+ memset( param, 0, 4 );
+ *( (uint16_t *) ( param + 4 ) ) = x;
+ *( (uint16_t *) ( param + 6 ) ) = y;
+ out = param + 8;
+ num_chars = 0;
+
+ while( i < len && num_chars < 0xFF ) {
+ int dx, dy, index;
+
+ if( ( index = query_glyph( buf[ i ] ) ) < 0 ) {
+ /* Cache miss. */
+ int bitmap_size;
+ FT_GlyphSlot slot;
+
+ if( ( index = replace_glyph( buf[ i ] ) ) < 0 )
+ /* Cache set full: spill the partial string to make
+ room for later glyphs. */
+ break;
+
+ if( FT_Load_Char( ftf, buf[ i ], FT_LOAD_RENDER |
+ FONT_LOAD_TARGET ) ||
+ ( bitmap_size = ( ( ftf->glyph->bitmap.width + 3 ) & ~3 ) *
+ ftf->glyph->bitmap.rows ) > sizeof data ) {
+ /* We couldn't load the character, or it was so
+ huge that it won't fit in an empty AddGlyph
+ data buffer. We'll have to send the server
+ an empty glyph and carry on. */
+ slot = NULL;
+ bitmap_size = 0;
+ } else
+ slot = ftf->glyph;
+
+ if( ( p - data ) + bitmap_size > sizeof data ||
+ num_glyphs == sizeof glyphids / sizeof *glyphids ) {
+ /* Can't fit this glyph into the existing request:
+ transmit what we have... */
+ xcb_render_add_glyphs( c, glyphset, num_glyphs, glyphids,
+ glyphs, p - data, data );
+
+ /* ...and start building another AddGlyph request. */
+ p = data;
+ num_glyphs = 0;
+ }
+
+ glyphids[ num_glyphs ] = index;
+
+ if( slot ) {
+ int y, width_pad;
+
+ glyphs[ num_glyphs ].width = slot->bitmap.width;
+ glyphs[ num_glyphs ].height = slot->bitmap.rows;
+ glyphs[ num_glyphs ].x = -slot->bitmap_left;
+ glyphs[ num_glyphs ].y = slot->bitmap_top;
+ glyphs[ num_glyphs ].x_off =
+ ( slot->advance.x + 0x20 ) >> 6;
+ glyphs[ num_glyphs ].y_off =
+ ( slot->advance.y + 0x20 ) >> 6;
+
+ width_pad = ( slot->bitmap.width + 3 ) & ~3;
+
+ for( y = 0; y < slot->bitmap.rows; y++ ) {
+ memcpy( p, slot->bitmap.buffer + y *
+ slot->bitmap.width, slot->bitmap.width );
+ p += width_pad;
+ }
+ } else {
+ glyphs[ num_glyphs ].width = 0;
+ glyphs[ num_glyphs ].height = 0;
+ glyphs[ num_glyphs ].x = 0;
+ glyphs[ num_glyphs ].y = 0;
+ glyphs[ num_glyphs ].x_off = 0;
+ glyphs[ num_glyphs ].y_off = 0;
+ }
+
+ num_glyphs++;
+ }
+
+ *( (uint16_t *) out ) = index;
+ out += 2;
+
+ query_metrics( buf[ i ], &dx, &dy );
+ x += dx;
+ y += dy;
+
+ num_chars++;
+ i++;
+ }
+
+ if( num_glyphs ) {
+ xcb_render_add_glyphs( c, glyphset, num_glyphs, glyphids, glyphs,
+ p - data, data );
+
+ p = data;
+ num_glyphs = 0;
+ }
+
+ param[ 0 ] = num_chars;
+
+ xcb_render_composite_glyphs_16( c, XCB_RENDER_PICT_OP_OVER, src, dest,
+ XCB_NONE, glyphset, 0, 0, out - param,
+ param );
+ }
+
+ return xcb_render_free_picture( c, dest );
+}
+
+extern void render_update_window( struct gwm_window *window ) {
+
+ if( !window->cleared )
+ xcb_clear_area( c, FALSE, window->w, window->update.x, window->update.y,
+ window->update.width, window->update.height );
+
+ if( window->type == WINDOW_FRAME ) {
+ char *name = window->u.frame.child->u.managed.name;
+
+ render_text( window->w, window->screen, window == focus_frame ?
+ COL_TITLE_ACTIVE : COL_TITLE_INACTIVE,
+ FRAME_BUTTON_SIZE + FRAME_BORDER_WIDTH * 3,
+ FONT_PIXEL_SIZE, name ? name : "(Untitled)",
+ &window->update );
+ } else if( window->type == WINDOW_FEEDBACK ) {
+ char *p, text[ 32 ];
+ int width;
+
+ sprintf( text, "%dx%d", window->u.feedback.fb_width,
+ window->u.feedback.fb_height );
+
+ for( width = 0, p = text; *p; p++ ) {
+ int dx, dy;
+
+ query_metrics( *p, &dx, &dy );
+ width += dx;
+ }
+
+ render_text( window->w, window->screen, COL_FEEDBACK_FORE,
+ ( FEEDBACK_WIDTH - width ) >> 1, 16, text,
+ &window->update );
+ }
+}
+
+static uint16_t luma( int col ) {
+
+ return ( decoration_cols[ col ][ 0 ] * 0x4C8BU +
+ decoration_cols[ col ][ 1 ] * 0x9646U +
+ decoration_cols[ col ][ 2 ] * 0x1D2FU ) >> 16;
+}
+
+static void handle_alloc_color( unsigned int sequence, void *reply,
+ xcb_generic_error_t *error,
+ union callback_param p ) {
+
+ xcb_alloc_color_reply_t *r = reply;
+ int screen = p.l >> 16, col = p.l & 0xFFFF;
+
+ if( error ) {
+ if( error->error_code != XCB_ALLOC )
+ show_error( error );
+
+ /* No standard RGB map available, and we couldn't allocate a shared
+ colour. Fall back to black or white. */
+ gwm_screens[ screen ].pixels[ col ] = luma( col ) >= 0x8000 ?
+ screens[ screen ]->white_pixel : screens[ screen ]->black_pixel;
+
+ free( error );
+ }
+
+ if( reply ) {
+ gwm_screens[ screen ].pixels[ col ] = r->pixel;
+
+ free( reply );
+ }
+}
+
+static void handle_get_default_map( unsigned int sequence, void *reply,
+ xcb_generic_error_t *error,
+ union callback_param p ) {
+
+ int col, got_cols = FALSE;
+
+ if( error ) {
+ show_error( error );
+
+ free( error );
+ }
+
+ if( reply ) {
+ xcb_get_property_reply_t *prop = reply;
+ struct std_cmap {
+ /* RGB_COLOR_MAP property -- see ICCCM 2.0, section 6.4. */
+ xcb_colormap_t colormap;
+ uint32_t red_max, red_mult, green_max, green_mult, blue_max,
+ blue_mult, base_pixel;
+ xcb_visualid_t visual_id;
+ uint32_t kill_id;
+ } *std_cmaps = xcb_get_property_value( prop );
+ int num_cmaps = ( xcb_get_property_value_length( prop ) << 2 ) /
+ sizeof *std_cmaps;
+ int i;
+
+ if( prop->format == 32 )
+ for( i = 0; i < num_cmaps; i++ )
+ if( std_cmaps[ i ].visual_id ==
+ gwm_screens[ p.l ].root_visual->visual_id ) {
+
+ for( col = 0; col < NUM_COLS; col++ ) {
+ int r0, g0, b0, r, g, b;
+
+ if( gwm_screens[ p.l ].root_visual->_class ==
+ XCB_VISUAL_CLASS_STATIC_GRAY ||
+ gwm_screens[ p.l ].root_visual->_class ==
+ XCB_VISUAL_CLASS_GRAY_SCALE ) {
+ r0 = luma( col );
+ g0 = b0 = 0;
+ } else {
+ r0 = decoration_cols[ col ][ 0 ];
+ g0 = decoration_cols[ col ][ 1 ];
+ b0 = decoration_cols[ col ][ 2 ];
+ }
+
+ r = ( r0 * std_cmaps[ i ].red_max + 0x8000 ) >> 16;
+ g = ( g0 * std_cmaps[ i ].green_max + 0x8000 ) >> 16;
+ b = ( b0 * std_cmaps[ i ].blue_max + 0x8000 ) >> 16;
+
+ gwm_screens[ p.l ].pixels[ col ] =
+ std_cmaps[ i ].base_pixel +
+ r * std_cmaps[ i ].red_mult +
+ g * std_cmaps[ i ].green_mult +
+ b * std_cmaps[ i ].blue_mult;
+ }
+
+ got_cols = TRUE;
+ break;
+ }
+
+ free( reply );
+ }
+
+ if( !got_cols )
+ /* No useful default maps found; try allocating directly. */
+ for( col = 0; col < NUM_COLS; col++ ) {
+ int r, g, b;
+ union callback_param cp;
+
+ if( gwm_screens[ p.l ].root_visual->_class ==
+ XCB_VISUAL_CLASS_STATIC_GRAY ||
+ gwm_screens[ p.l ].root_visual->_class ==
+ XCB_VISUAL_CLASS_GRAY_SCALE )
+ r = g = b = luma( col );
+ else {
+ r = decoration_cols[ col ][ 0 ];
+ g = decoration_cols[ col ][ 1 ];
+ b = decoration_cols[ col ][ 2 ];
+ }
+
+ cp.l = ( p.l << 16 ) | col;
+ /* Ideally, we would like a guarantee that this operation will
+ complete before the pixel values are ever looked up. In
+ practice, it will, and it is too messy to insert a
+ sync_with_callback() before every single pixel reference. */
+ handle_async_reply( xcb_alloc_color(
+ c, screens[ p.l ]->default_colormap,
+ r, g, b ).sequence, handle_alloc_color,
+ cp );
+ }
+}
+
+static void decorate_compat_init( void ) {
+
+ int i;
+ const char *cursor_font_name = "cursor";
+ xcb_font_t cursor_font;
+ static const int cursor_glyphs[ NUM_CURSORS ] = {
+ 68, /* CURSOR_ARROW */
+ 88, /* CURSOR_DESTROY */
+ 134, /* CURSOR_TL */
+ 138, /* CURSOR_T */
+ 136, /* CURSOR_TR */
+ 70, /* CURSOR_L */
+ 52, /* CURSOR_C */
+ 96, /* CURSOR_R */
+ 12, /* CURSOR_BL */
+ 16, /* CURSOR_B */
+ 14 /* CURSOR_BR */
+ };
+
+ cursor_font = xcb_generate_id( c );
+ xcb_open_font( c, cursor_font, strlen( cursor_font_name ),
+ cursor_font_name );
+ for( i = 0; i < NUM_CURSORS; i++ ) {
+ cursors[ i ] = xcb_generate_id( c );
+ xcb_create_glyph_cursor( c, cursors[ i ], cursor_font, cursor_font,
+ cursor_glyphs[ i ], cursor_glyphs[ i ] | 1,
+ 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF );
+ }
+ xcb_close_font( c, cursor_font );
+
+ /* Retrieve any RGB_DEFAULT_MAP properties on the roots. */
+ for( i = 0; i < num_screens; i++ ) {
+ union callback_param cp;
+
+ cp.l = i;
+ handle_async_reply( xcb_get_property( c, FALSE, screens[ i ]->root,
+ RGB_DEFAULT_MAP, RGB_COLOR_MAP,
+ 0, 0x10000 ).sequence,
+ handle_get_default_map, cp );
+ }
+}
+
+extern void decorate_render_init( void ) {
+
+ FcPattern *pat, *font;
+ FcResult r;
+ FcValue v;
+ FT_Error err;
+ char *filename;
+ int i, index;
+ xcb_pixmap_t pixmap;
+ uint32_t n;
+
+ FcInit();
+
+ pat = FcPatternBuild( NULL, FC_FAMILY, FcTypeString, FONT_FAMILY,
+ FC_WEIGHT, FcTypeInteger, FONT_WEIGHT,
+ FC_PIXEL_SIZE, FcTypeDouble, (double) FONT_PIXEL_SIZE,
+ NULL );
+
+ FcConfigSubstitute( NULL, pat, FcMatchPattern );
+
+ FcDefaultSubstitute( pat );
+
+ font = FcFontMatch( NULL, pat, &r );
+
+ FcPatternGet( font, FC_FILE, 0, &v );
+ filename = (char *) v.u.s;
+ FcPatternGet( font, FC_INDEX, 0, &v );
+ index = v.u.i;
+
+ if( ( err = FT_Init_FreeType( &ftl ) ) )
+ fatal( "typeface initialisation error %d", err );
+
+ if( ( err = FT_New_Face( ftl, filename, index, &ftf ) ) )
+ fatal( "%s: could not load font (%d)", filename, err );
+
+ FcPatternDestroy( pat );
+ FcPatternDestroy( font );
+
+ FcFini();
+
+ if( ( err = FT_Set_Char_Size( ftf, 0, FONT_PIXEL_SIZE << 6, 0, 0 ) ) )
+ fatal( "%s: could not scale font (%d)", filename, err );
+
+ if( ( err = FT_Select_Charmap( ftf, FT_ENCODING_UNICODE ) ) )
+ fatal( "%s: could not select character map (%d)", filename, err );
+
+ glyphset = xcb_generate_id( c );
+ xcb_render_create_glyph_set( c, glyphset, fmt_a8 );
+
+ pixmap = xcb_generate_id( c );
+
+ pictures = xmalloc( num_screens * sizeof *pictures );
+
+ n = XCB_RENDER_REPEAT_NORMAL;
+ for( i = 0; i < num_screens; i++ ) {
+ xcb_create_pixmap( c, screens[ i ]->root_depth, pixmap,
+ screens[ i ]->root, 1, 1 );
+
+ pictures[ i ].pic = xcb_generate_id( c );
+ xcb_render_create_picture( c, pictures[ i ].pic, pixmap,
+ gwm_screens[ i ].root_pictformat,
+ XCB_RENDER_CP_REPEAT, &n );
+ pictures[ i ].col = -1;
+
+ xcb_free_pixmap( c, pixmap );
+ }
+
+ decorate_compat_init(); /* FIXME this is for pixel values (backgrounds)
+ and cursors */
+}
+
+extern void decorate_render_done( void ) {
+
+#if DEBUG
+ free( pictures );
+ FT_Done_Face( ftf );
+ FT_Done_FreeType( ftl );
+#endif
+}
diff --git a/decorate-render.h b/decorate-render.h
new file mode 100644
index 0000000..e607544
--- /dev/null
+++ b/decorate-render.h
@@ -0,0 +1,8 @@
+#ifndef DECORATE_RENDER_H
+#define DECORATE_RENDER_H
+
+extern void render_update_window( struct gwm_window *window );
+extern void decorate_render_init( void );
+extern void decorate_render_done( void );
+
+#endif
diff --git a/gwm.c b/gwm.c
index ba30d72..e924d0a 100644
--- a/gwm.c
+++ b/gwm.c
@@ -47,7 +47,6 @@
#if USE_DAMAGE
#include <xcb/damage.h>
#endif
-#include <xcb/xproto.h>
#if USE_RENDER
#include <xcb/render.h>
#endif
@@ -64,6 +63,9 @@
#include "button.h"
#include "decorate-core.h"
+#if USE_RENDER
+#include "decorate-render.h"
+#endif
#include "frame.h"
#include "keyboard.h"
#include "managed.h"
@@ -127,6 +129,11 @@ int num_screens;
xcb_screen_t **screens;
struct gwm_screen *gwm_screens;
+#if USE_RENDER
+xcb_render_pictformat_t fmt_rgba8, fmt_a8;
+xcb_render_directformat_t dfmt_rgba8, dfmt_a8;
+#endif
+
xcb_cursor_t cursors[ NUM_CURSORS ];
struct gwm_window *fake_window, *focus_frame;
@@ -417,6 +424,20 @@ extern void show_error( xcb_generic_error_t *error ) {
bad_value_str = "";
break;
}
+
+#if USE_RENDER
+ if( have_extension[ EXT_RENDER ] &&
+ error->error_code >= extension_error[ EXT_RENDER ] &&
+ error->error_code <= extension_error[ EXT_RENDER ] +
+ XCB_RENDER_GLYPH ) {
+ static const char *render_strings[ XCB_RENDER_GLYPH + 1 ] = {
+ "PictFormat", "Picture", "PictOp", "GlyphSet", "Glyph"
+ };
+
+ type = render_strings[ error->error_code -
+ extension_error[ EXT_RENDER ] ];
+ }
+#endif
warning( "unexpected error (type %s, sequence %d; opcode %d %d%s)", type,
error->sequence, verror->major_opcode, verror->minor_opcode,
@@ -777,7 +798,8 @@ extern xcb_generic_event_t *wait_for_event( void ) {
#endif
check_async_callbacks(); /* must poll for events first, since
- polling replies won't dequeue them */
+ polling replies won't dequeue them */
+
if( ev )
return ev;
@@ -1741,7 +1763,7 @@ static void setup_display( void ) {
xcb_client_message_event_t msg;
xcb_void_cookie_t *cookies;
xcb_query_tree_cookie_t *tree_cookies;
-
+
table_init( &windows );
table_init( &update_windows );
@@ -1825,13 +1847,6 @@ static void setup_display( void ) {
}
}
- decorate_core_init();
- update_window = core_update_window;
-
- /* FIXME For each extension we have, query if its version is recent
- enough if neccesary. (SHAPE is OK -- version 1.0 is enough, and
- it's the only extension we bother with so far.) */
-
for( i = 0; i < NUM_ATOMS; i++ ) {
xcb_intern_atom_reply_t *r =
xcb_intern_atom_reply( c, atom_cookies[ i ], NULL );
@@ -1865,6 +1880,111 @@ static void setup_display( void ) {
free( r );
}
+#if USE_RENDER
+ if( have_extension[ EXT_RENDER ] ) {
+ xcb_render_query_version_cookie_t render_cookie;
+ xcb_render_query_pict_formats_cookie_t formats_cookie;
+ xcb_render_query_version_reply_t *render_reply;
+ xcb_render_query_pict_formats_reply_t *formats_reply;
+ xcb_render_pictforminfo_t *formats;
+ int i;
+ xcb_render_pictscreen_iterator_t siter;
+
+ render_cookie = xcb_render_query_version( c, XCB_RENDER_MAJOR_VERSION,
+ XCB_RENDER_MINOR_VERSION );
+
+ formats_cookie = xcb_render_query_pict_formats( c );
+
+ xcb_flush( c ); /* BLOCK */
+
+ render_reply = xcb_render_query_version_reply( c, render_cookie, NULL );
+
+ if( !render_reply ) {
+ have_extension[ EXT_RENDER ] = FALSE;
+ goto no_render;
+ }
+
+ if( !render_reply->major_version && render_reply->minor_version < 1 ) {
+ /* We need at least version 0.1, for FillRectangles support. It's
+ too inconvenient to initialise pictures to a constant colour
+ without it (which seems to have been an oversight in the
+ original specification of the protocol). */
+ have_extension[ EXT_RENDER ] = FALSE;
+
+ free( render_reply );
+
+ goto no_render;
+ }
+
+ free( render_reply );
+
+ formats_reply = xcb_render_query_pict_formats_reply( c, formats_cookie,
+ NULL );
+
+ formats = xcb_render_query_pict_formats_formats( formats_reply );
+
+ for( i = 0; i < formats_reply->num_formats; i++ )
+ if( formats[ i ].type == XCB_RENDER_PICT_TYPE_DIRECT &&
+ formats[ i ].direct.alpha_mask == 0xFF ) {
+ if( !formats[ i ].direct.red_mask &&
+ !formats[ i ].direct.green_mask &&
+ !formats[ i ].direct.blue_mask ) {
+ fmt_a8 = formats[ i ].id;
+ memcpy( &dfmt_a8, &formats[ i ].direct, sizeof dfmt_a8 );
+ } else if( formats[ i ].direct.red_mask == 0xFF &&
+ formats[ i ].direct.green_mask == 0xFF &&
+ formats[ i ].direct.blue_mask == 0xFF ) {
+ fmt_rgba8 = formats[ i ].id;
+ memcpy( &dfmt_rgba8, &formats[ i ].direct,
+ sizeof dfmt_rgba8 );
+ }
+ }
+
+ if( !fmt_a8 || !fmt_rgba8 )
+ /* These two pictformats are guaranteed by the RENDER protocol
+ (section 7). We can't cope without them. */
+ have_extension[ EXT_RENDER ] = FALSE;
+
+ for( i = 0, siter = xcb_render_query_pict_formats_screens_iterator(
+ formats_reply ); siter.rem;
+ i++, xcb_render_pictscreen_next( &siter ) ) {
+ xcb_render_pictdepth_iterator_t diter;
+
+ for( diter = xcb_render_pictscreen_depths_iterator( siter.data );
+ diter.rem; xcb_render_pictdepth_next( &diter ) ) {
+ xcb_render_pictvisual_iterator_t viter;
+
+ for( viter = xcb_render_pictdepth_visuals_iterator(
+ diter.data ); viter.rem;
+ xcb_render_pictvisual_next( &viter ) )
+ if( viter.data->visual ==
+ gwm_screens[ i ].root_visual->visual_id ) {
+ gwm_screens[ i ].root_pictformat = viter.data->format;
+ goto next_screen;
+ }
+ }
+ next_screen:
+ ;
+ }
+
+ free( formats_reply );
+
+ no_render:
+ ;
+ }
+#endif
+
+#if USE_RENDER
+ if( have_extension[ EXT_RENDER ] ) {
+ decorate_render_init();
+ update_window = render_update_window;
+ } else
+#endif
+ {
+ decorate_core_init();
+ update_window = core_update_window;
+ }
+
/* Listen for a selection timestamp. */
for(;;) {
/* BLOCK */
@@ -2501,13 +2621,18 @@ retry:
xcb_set_input_focus( c, XCB_INPUT_FOCUS_NONE, XCB_INPUT_FOCUS_POINTER_ROOT,
XCB_CURRENT_TIME );
- decorate_core_done();
-
n = 0;
for( i = 0; i < num_screens; i++ )
xcb_change_window_attributes( c, screens[ i ]->root,
XCB_CW_EVENT_MASK, &n );
+#if USE_RENDER
+ if( have_extension[ EXT_RENDER ] )
+ decorate_render_done();
+ else
+#endif
+ decorate_core_done();
+
/* A pointless request to generate a reply, so we'll know when we've
processed everything the server will send us. */
cookie = xcb_intern_atom( c, TRUE, 4, "ATOM" );
diff --git a/gwm.h b/gwm.h
index 0fcb1d6..4758c9b 100644
--- a/gwm.h
+++ b/gwm.h
@@ -122,6 +122,11 @@ extern int have_extension[ EXTENSIONS_SIZE ];
extern uint8_t extension_event[ EXTENSIONS_SIZE ],
extension_error[ EXTENSIONS_SIZE ];
+#if USE_RENDER && defined( XCB_RENDER_MAJOR_VERSION )
+extern xcb_render_pictformat_t fmt_rgba8, fmt_a8;
+extern xcb_render_directformat_t dfmt_rgba8, dfmt_a8;
+#endif
+
enum decoration_col {
COL_FRAME_ACTIVE,
COL_FRAME_INACTIVE,
@@ -144,6 +149,9 @@ struct gwm_screen {
xcb_colormap_t cmap; /* the most recently installed colormap */
xcb_timestamp_t cmap_time; /* the time at which it was installed */
uint32_t pixels[ NUM_COLS ]; /* pixel values in the default cmap */
+#if USE_RENDER
+ uint32_t root_pictformat;
+#endif
};
extern struct gwm_screen *gwm_screens;