xwords/xwords4/linux/gtkdraw.c
Eric House d293517e7c toward compiling with gcc8
My VSIZE is no longer legal, and apparently there's no workaround (no
way to safely figure the length of an array whose size is known at
compile time.) To avoid the risk of duplicating little constants, added
macros that define length in a way a new VSIZE can pick it up. Couldn't
make that work for struct field arrays, however, so there I'm using
constants.
2019-01-28 17:24:53 -08:00

1581 lines
46 KiB
C

/* -*- compile-command: "make MEMDEBUG=TRUE -j5"; -*- */
/*
* Copyright 1997 - 2017 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef PLATFORM_GTK
#include <stdlib.h>
#include <stdio.h>
#undef GDK_DISABLE_DEPRECATED
#include <gdk/gdk.h>
#include "gtkboard.h"
#include "draw.h"
#include "board.h"
#include "linuxmain.h"
typedef enum {
XP_GTK_JUST_NONE
,XP_GTK_JUST_CENTER
,XP_GTK_JUST_TOPLEFT
,XP_GTK_JUST_BOTTOMRIGHT
} XP_GTK_JUST;
typedef struct FontPerSize {
unsigned int ht;
PangoFontDescription* fontdesc;
PangoLayout* layout;
} FontPerSize;
static void gtk_draw_measureScoreText( DrawCtx* p_dctx, const XP_Rect* bounds,
const DrawScoreInfo* dsi,
XP_U16* widthP, XP_U16* heightP );
static gdouble figureColor( int in );
/* static GdkGC* newGCForColor( GdkWindow* window, XP_Color* newC ); */
static void
gtkInsetRect( XP_Rect* r, short i )
{
r->top += i;
r->left += i;
i *= 2;
XP_ASSERT( r->height >= i && r->width >= i );
r->width -= i;
r->height -= i;
} /* gtkInsetRect */
#define GTKMIN_W_HT 12
#ifdef USE_CAIRO
# define XP_UNUSED_CAIRO(var) UNUSED__ ## var __attribute__((unused))
# define GDKDRAWABLE void
# define GDKGC void
# define GDKCOLORMAP void
#define LOG_CAIRO_PENDING() XP_LOGF( "%s(): CAIRO work pending", __func__ )
#else
# define XP_UNUSED_CAIRO(var) var
# define GDKCOLORMAP GdkColormap
#endif
static XP_Bool
initCairo( GtkDrawCtx* dctx )
{
/* XP_LOGF( "%s(dctx=%p)", __func__, dctx ); */
XP_ASSERT( !dctx->_cairo );
cairo_t* cairo = NULL;
if ( !!dctx->surface ) {
cairo = cairo_create( dctx->surface );
cairo_surface_destroy( dctx->surface );
// XP_ASSERT( 0 );
} else if ( !!dctx->drawing_area ) {
#ifdef GDK_AVAILABLE_IN_3_22
GdkWindow* window = gtk_widget_get_window( dctx->drawing_area );
const cairo_region_t* region = gdk_window_get_visible_region( window );
dctx->dc = gdk_window_begin_draw_frame( window, region );
cairo = gdk_drawing_context_get_cairo_context( dctx->dc );
#else
cairo = gdk_cairo_create( gtk_widget_get_window(dctx->drawing_area) );
#endif
} else {
XP_ASSERT( 0 );
}
XP_Bool inited = !!cairo;
if ( inited ) {
dctx->_cairo = cairo;
if ( !!dctx->surface ) {
cairo_set_line_width( cairo, 0.1 );
} else {
cairo_set_line_width( cairo, 1.0 );
}
cairo_set_line_cap( cairo, CAIRO_LINE_CAP_SQUARE );
}
return inited;
}
static void
destroyCairo( GtkDrawCtx* dctx )
{
/* XP_LOGF( "%s(dctx=%p)", __func__, dctx ); */
XP_ASSERT( !!dctx->_cairo );
if ( !!dctx->surface ) {
XP_LOGF( "%s(): have surface; doing nothing", __func__ );
} else {
#ifdef GDK_AVAILABLE_IN_3_22
GdkWindow* window = gtk_widget_get_window( dctx->drawing_area );
gdk_window_end_draw_frame( window, dctx->dc );
#else
cairo_destroy( dctx->_cairo );
#endif
}
dctx->_cairo = NULL;
}
static XP_Bool
haveCairo( const GtkDrawCtx* dctx )
{
return !!dctx->_cairo;
}
static cairo_t*
getCairo( const GtkDrawCtx* dctx )
{
XP_ASSERT( !!dctx->_cairo );
return dctx->_cairo;
}
static void
draw_rectangle( const GtkDrawCtx* dctx,
gboolean fill, gint left, gint top, gint width,
gint height )
{
#ifdef USE_CAIRO
cairo_t *cr = getCairo( dctx );
cairo_rectangle( cr, left, top, width, height );
cairo_stroke_preserve( cr );
if ( fill ) {
cairo_fill( cr );
} else {
cairo_stroke( cr );
}
/* } else { */
/* cairo_stroke( dctx->cairo ); */
/* } */
#else
dctx = dctx;
gdk_draw_rectangle( drawable, gc, fill, left, top, width, height );
#endif
}
static void
gtkSetForeground( const GtkDrawCtx* dctx, const GdkRGBA* color )
{
#ifdef USE_CAIRO
gdk_cairo_set_source_rgba( getCairo(dctx), color );
#else
gdk_gc_set_foreground( dctx->drawGC, color );
#endif
}
static void
gtkFillRect( GtkDrawCtx* dctx, const XP_Rect* rect, const GdkRGBA* color )
{
gtkSetForeground( dctx, color );
draw_rectangle( dctx, TRUE, rect->left, rect->top,
rect->width, rect->height );
}
static void
set_color_cairo( const GtkDrawCtx* dctx, unsigned short red,
unsigned short green, unsigned short blue )
{
GdkRGBA color = { figureColor(red),
figureColor(green),
figureColor(blue),
1.0 };
gdk_cairo_set_source_rgba( getCairo(dctx), &color );
}
static void
gtkEraseRect( const GtkDrawCtx* dctx, const XP_Rect* rect )
{
set_color_cairo( dctx, 0xFFFF, 0xFFFF, 0xFFFF );
// const GtkStyle* style = gtk_widget_get_style( dctx->drawing_area );
draw_rectangle( dctx, TRUE, rect->left, rect->top,
rect->width, rect->height );
} /* gtkEraseRect */
#ifdef DRAW_WITH_PRIMITIVES
static void
gtk_prim_draw_setClip( DrawCtx* p_dctx, XP_Rect* newClip, XP_Rect* oldClip)
{
} /* gtk_prim_draw_setClip */
static void
gtk_prim_draw_frameRect( DrawCtx* p_dctx, XP_Rect* rect )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
frameRect( dctx, rect );
} /* gtk_prim_draw_frameRect */
static void
gtk_prim_draw_invertRect( DrawCtx* p_dctx, XP_Rect* rect )
{
/* not sure you can do this on GTK!! */
} /* gtk_prim_draw_invertRect */
static void
gtk_prim_draw_clearRect( DrawCtx* p_dctx, XP_Rect* rect )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
gtkEraseRect( dctx, rect );
} /* gtk_prim_draw_clearRect */
static void
gtk_prim_draw_drawString( DrawCtx* p_dctx, XP_UCHAR* str,
XP_U16 x, XP_U16 y )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_U16 fontHeight = 10; /* FIX ME */
gdk_draw_string( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
x, y + fontHeight, str );
} /* gtk_prim_draw_drawString */
static void
gtk_prim_draw_drawBitmap( DrawCtx* p_dctx, XP_Bitmap bm,
XP_U16 x, XP_U16 y )
{
} /* gtk_prim_draw_drawBitmap */
static void
gtk_prim_draw_measureText( DrawCtx* p_dctx, XP_UCHAR* str,
XP_U16* widthP, XP_U16* heightP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
gint len = strlen(str);
gint width = gdk_text_measure( dctx->gdkFont, str, len );
*widthP = width;
*heightP = 12; /* ??? :-) */
} /* gtk_prim_draw_measureText */
#endif /* DRAW_WITH_PRIMITIVES */
static gint
compForHt( gconstpointer a,
gconstpointer b )
{
FontPerSize* fps1 = (FontPerSize*)a;
FontPerSize* fps2 = (FontPerSize*)b;
return fps1->ht - fps2->ht;
}
static PangoLayout*
layout_for_ht( GtkDrawCtx* dctx, XP_U16 ht )
{
PangoLayout* layout = NULL;
/* Try to find a cached layout. Otherwise create a new one. */
FontPerSize fps = { .ht = ht };
GList* gl = g_list_find_custom( dctx->fontsPerSize, &fps,
compForHt );
if ( NULL != gl ) {
layout = g_object_ref(((FontPerSize*)gl->data)->layout);
}
if ( NULL == layout ) {
FontPerSize* fps = g_malloc( sizeof(*fps) );
dctx->fontsPerSize = g_list_insert( dctx->fontsPerSize, fps, 0 );
char font[32];
snprintf( font, sizeof(font), "helvetica normal %dpx", ht );
PangoContext* pc = pango_cairo_create_context( getCairo(dctx) );
layout = pango_layout_new( pc );
g_object_unref( pc );
fps->fontdesc = pango_font_description_from_string( font );
pango_layout_set_font_description( layout, fps->fontdesc );
fps->layout = g_object_ref( layout );
/* This only happens first time??? */
pango_layout_set_alignment( layout, PANGO_ALIGN_CENTER );
fps->ht = ht;
}
return layout;
} /* layout_for_ht */
static void
draw_string_at( GtkDrawCtx* dctx, PangoLayout* layout,
const XP_UCHAR* str, XP_U16 fontHt,
const XP_Rect* where, XP_GTK_JUST just,
const GdkRGBA* frground,
const GdkRGBA* bkgrnd )
{
// XP_LOGF( "%s(%s, %d, %d)", __func__, str, where->left, where->top );
gint xx = where->left;
gint yy = where->top;
#ifdef USE_CAIRO
cairo_t* cr = getCairo( dctx );
#endif
gdk_cairo_set_source_rgba( cr, frground );
if ( !!layout ) {
g_object_ref( layout );
} else {
layout = layout_for_ht( dctx, fontHt );
}
pango_layout_set_text( layout, (char*)str, XP_STRLEN(str) );
if ( just != XP_GTK_JUST_NONE ) {
int width, height;
pango_layout_get_pixel_size( layout, &width, &height );
switch( just ) {
case XP_GTK_JUST_CENTER:
xx += (where->width - width) / 2;
yy += (where->height - height) / 2;
break;
case XP_GTK_JUST_BOTTOMRIGHT:
xx += where->width - width;
yy += where->height - height;
break;
case XP_GTK_JUST_TOPLEFT:
default:
/* nothing to do?? */
break;
}
}
#ifdef USE_CAIRO
XP_USE(bkgrnd);
cairo_save( cr );
cairo_translate( cr, xx, yy );
pango_cairo_show_layout( cr, layout );
cairo_restore( cr );
#else
gdk_draw_layout_with_colors( DRAW_WHAT(dctx), dctx->drawGC,
xx, yy, layout,
frground, bkgrnd );
#endif
g_object_unref(layout);
} /* draw_string_at */
static void
drawBitmapFromLBS( GtkDrawCtx* dctx, const XP_Bitmap bm, const XP_Rect* rect )
{
LinuxBMStruct* lbs = (LinuxBMStruct*)bm;
gint x, y;
XP_U8* bp;
XP_U16 i;
XP_S16 nBytes;
XP_U16 nCols, nRows;
nCols = lbs->nCols;
nRows = lbs->nRows;
bp = (XP_U8*)(lbs + 1); /* point to the bitmap data */
nBytes = lbs->nBytes;
#ifdef USE_CAIRO
draw_rectangle( dctx, TRUE, 0, 0, nCols, nRows );
#else
const GtkStyle* style = gtk_widget_get_style( dctx->drawing_area );
GdkPixmap* pm = gdk_pixmap_new( DRAW_WHAT(dctx), nCols, nRows, -1 );
draw_rectangle( dctx, pm, style->white_gc, TRUE, 0, 0, nCols, nRows );
#endif
x = 0;
y = 0;
while ( nBytes-- ) {
XP_U8 byte = *bp++;
for ( i = 0; i < 8; ++i ) {
XP_Bool draw = ((byte & 0x80) != 0);
if ( draw ) {
#ifdef USE_CAIRO
LOG_CAIRO_PENDING();
#else
gdk_draw_point( pm, style->black_gc, x, y );
#endif
}
byte <<= 1;
if ( ++x == nCols ) {
x = 0;
if ( ++y == nRows ) {
break;
}
}
}
}
XP_ASSERT( nBytes == -1 ); /* else we're out of sync */
#ifdef USE_CAIRO
rect = rect;
LOG_CAIRO_PENDING();
#else
gdk_draw_drawable( DRAW_WHAT(dctx),
dctx->drawGC,
(GdkDrawable*)pm, 0, 0,
rect->left+2,
rect->top+2,
lbs->nCols,
lbs->nRows );
g_object_unref( pm );
#endif
} /* drawBitmapFromLBS */
static void
freer( gpointer data, gpointer XP_UNUSED(user_data) )
{
FontPerSize* fps = (FontPerSize*)data;
pango_font_description_free( fps->fontdesc );
g_object_unref( fps->layout );
g_free( fps );
}
static void
gtk_draw_destroyCtxt( DrawCtx* p_dctx )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
GtkAllocation alloc;
gtk_widget_get_allocation( dctx->drawing_area, &alloc );
#ifdef USE_CAIRO
draw_rectangle( dctx, TRUE, 0, 0, alloc.width, alloc.height );
#else
const GtkStyle* style = gtk_widget_get_style( dctx->drawing_area );
draw_rectangle( dctx, DRAW_WHAT(dctx), style->white_gc, TRUE,
0, 0, alloc.width, alloc.height );
#endif
g_list_foreach( dctx->fontsPerSize, freer, NULL );
g_list_free( dctx->fontsPerSize );
} /* gtk_draw_destroyCtxt */
static void
gtk_draw_dictChanged( DrawCtx* XP_UNUSED(p_dctx),
XP_S16 XP_UNUSED(playerNum),
const DictionaryCtxt* XP_UNUSED(dict) )
{
}
static XP_Bool
gtk_draw_beginDraw( DrawCtx* p_dctx )
{
#ifdef USE_CAIRO
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
return initCairo( dctx );
#else
fix this
#endif
}
static void
gtk_draw_endDraw( DrawCtx* p_dctx )
{
#ifdef USE_CAIRO
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
destroyCairo( dctx );
#endif
}
static XP_Bool
gtk_draw_boardBegin( DrawCtx* p_dctx, const XP_Rect* rect,
XP_U16 width, XP_U16 height,
DrawFocusState XP_UNUSED(dfs) )
{
GdkRectangle gdkrect;
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
dctx->cellWidth = width;
dctx->cellHeight = height;
gtkSetForeground( dctx, &dctx->black );
gdkrect = *(GdkRectangle*)rect;
++gdkrect.width;
++gdkrect.height;
return XP_TRUE;
} /* gtk_draw_boardBegin */
static void
gtk_draw_objFinished( DrawCtx* XP_UNUSED(p_dctx),
BoardObjectType XP_UNUSED(typ),
const XP_Rect* XP_UNUSED(rect),
DrawFocusState XP_UNUSED(dfs) )
{
} /* gtk_draw_objFinished */
static XP_Bool
gtk_draw_vertScrollBoard( DrawCtx* p_dctx, XP_Rect* rect,
XP_S16 dist, DrawFocusState XP_UNUSED(dfs) )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Bool down = dist <= 0;
gint ysrc, ydest;
if ( down ) {
ysrc = rect->top;
dist = -dist; /* make it positive */
ydest = ysrc + dist;
} else {
ydest = rect->top;
ysrc = ydest + dist;
}
#ifdef USE_CAIRO
dctx = dctx;
#else
gdk_draw_drawable( DRAW_WHAT(dctx),
dctx->drawGC,
DRAW_WHAT(dctx),
rect->left,
ysrc,
rect->left,
ydest,
rect->width,
rect->height - dist );
#endif
if ( !down ) {
rect->top += rect->height - dist;
}
rect->height = dist;
/* XP_LOGF( "%s=>(%d,%d,%d,%d)", __func__, rect->left, rect->top, */
/* rect->width, rect->height ); */
return XP_TRUE;
} /* gtk_draw_vertScrollBoard */
static void
drawHintBorders( GtkDrawCtx* dctx, const XP_Rect* rect, HintAtts hintAtts)
{
if ( hintAtts != HINT_BORDER_NONE && hintAtts != HINT_BORDER_CENTER ) {
XP_Rect lrect = *rect;
gtkInsetRect( &lrect, 1 );
gtkSetForeground( dctx, &dctx->black );
if ( (hintAtts & HINT_BORDER_LEFT) != 0 ) {
draw_rectangle( dctx, FALSE, lrect.left, lrect.top,
0, lrect.height);
}
if ( (hintAtts & HINT_BORDER_TOP) != 0 ) {
draw_rectangle( dctx, FALSE, lrect.left, lrect.top,
lrect.width, 0/*rectInset.height*/);
}
if ( (hintAtts & HINT_BORDER_RIGHT) != 0 ) {
draw_rectangle( dctx, FALSE, lrect.left+lrect.width,
lrect.top,
0, lrect.height);
}
if ( (hintAtts & HINT_BORDER_BOTTOM) != 0 ) {
draw_rectangle( dctx, FALSE, lrect.left,
lrect.top+lrect.height,
lrect.width, 0 );
}
}
}
#ifdef XWFEATURE_CROSSHAIRS
static void
drawCrosshairs( GtkDrawCtx* dctx, const XP_Rect* rect, CellFlags flags )
{
XP_Rect hairRect;
if ( 0 != (flags & CELL_CROSSHOR) ) {
hairRect = *rect;
hairRect.height /= 3;
hairRect.top += hairRect.height;
gtkFillRect( dctx, &hairRect, &dctx->cursor );
}
if ( 0 != (flags & CELL_CROSSVERT) ) {
hairRect = *rect;
hairRect.width /= 3;
hairRect.left += hairRect.width;
gtkFillRect( dctx, &hairRect, &dctx->cursor );
}
}
#else
# define drawCrosshairs( a, b, c )
#endif
static XP_Bool
gtk_draw_drawCell( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* letter,
const XP_Bitmaps* bitmaps, Tile XP_UNUSED(tile),
XP_U16 XP_UNUSED(value), XP_S16 owner, XWBonusType bonus,
HintAtts hintAtts, CellFlags flags )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect rectInset = *rect;
GtkGameGlobals* globals = dctx->globals;
XP_Bool showGrid = globals->gridOn;
XP_Bool highlight = (flags & CELL_HIGHLIGHT) != 0;
GdkRGBA* cursor =
((flags & CELL_ISCURSOR) != 0) ? &dctx->cursor : NULL;
gtkEraseRect( dctx, rect );
gtkInsetRect( &rectInset, 1 );
#ifdef USE_CAIRO
cairo_t* cr = getCairo( dctx );
#endif
if ( showGrid ) {
#ifdef USE_CAIRO
cairo_set_source_rgb( cr, 0, 0, 0 );
#else
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
#endif
draw_rectangle( dctx, FALSE, rect->left, rect->top,
rect->width, rect->height );
}
/* We draw just an empty, potentially colored, square IFF there's nothing
in the cell or if CELL_DRAGSRC is set */
if ( (flags & (CELL_DRAGSRC|CELL_ISEMPTY)) != 0 ) {
if ( !!cursor || bonus != BONUS_NONE ) {
GdkRGBA* foreground;
if ( !!cursor ) {
foreground = cursor;
} else if ( bonus != BONUS_NONE ) {
foreground = &dctx->bonusColors[bonus-1];
} else {
foreground = &dctx->white;
}
if ( !!foreground ) {
#ifdef USE_CAIRO
gtkFillRect( dctx, &rectInset, foreground );
// gdk_cairo_set_source_rgba( cr, foreground );
#else
gdk_gc_set_foreground( dctx->drawGC, foreground );
draw_rectangle( dctx, DRAW_WHAT(dctx), NULL, TRUE,
rectInset.left, rectInset.top,
rectInset.width+1, rectInset.height+1 );
#endif
}
}
if ( (flags & CELL_ISSTAR) != 0 ) {
draw_string_at( dctx, NULL, "*", dctx->cellHeight, rect,
XP_GTK_JUST_CENTER, &dctx->black, NULL );
}
} else if ( !!bitmaps && !!bitmaps->bmps[0] ) {
drawBitmapFromLBS( dctx, bitmaps->bmps[0], rect );
} else if ( !!letter ) {
XP_Bool isBlank = (flags & CELL_ISBLANK) != 0;
GdkRGBA* foreground;
if ( cursor ) {
gtkSetForeground( dctx, cursor );
} else if ( !highlight ) {
gtkSetForeground( dctx, &dctx->tileBack );
}
draw_rectangle( dctx, TRUE, rectInset.left, rectInset.top,
rectInset.width+1, rectInset.height+1 );
if ( isBlank && 0 == strcmp("_",letter ) ) {
letter = "?";
isBlank = XP_FALSE;
}
foreground = highlight? &dctx->white : &dctx->playerColors[owner];
draw_string_at( dctx, NULL, letter, dctx->cellHeight, &rectInset,
XP_GTK_JUST_CENTER, foreground, cursor );
if ( isBlank ) {
#ifdef USE_CAIRO
#else
gdk_draw_arc( DRAW_WHAT(dctx), dctx->drawGC,
0, /* filled */
rect->left, /* x */
rect->top, /* y */
rect->width,/*width, */
rect->height,/*width, */
0, 360*64 );
#endif
}
}
drawHintBorders( dctx, rect, hintAtts );
drawCrosshairs( dctx, rect, flags );
return XP_TRUE;
} /* gtk_draw_drawCell */
static void
gtk_draw_invertCell( DrawCtx* XP_UNUSED(p_dctx),
const XP_Rect* XP_UNUSED(rect) )
{
/* GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; */
/* (void)gtk_draw_drawMiniWindow( p_dctx, "f", rect); */
/* GdkGCValues values; */
/* gdk_gc_get_values( dctx->drawGC, &values ); */
/* gdk_gc_set_function( dctx->drawGC, GDK_INVERT ); */
/* gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)rect ); */
/* draw_rectangle( DRAW_WHAT(dctx), dctx->drawGC, */
/* TRUE, rect->left, rect->top, */
/* rect->width, rect->height ); */
/* gdk_gc_set_function( dctx->drawGC, values.function ); */
} /* gtk_draw_invertCell */
static XP_Bool
gtk_draw_trayBegin( DrawCtx* p_dctx, const XP_Rect* XP_UNUSED(rect),
XP_U16 owner, XP_S16 XP_UNUSED(owner),
DrawFocusState XP_UNUSED(dfs) )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Bool doDraw = !dctx->surface;
if ( doDraw ) {
dctx->trayOwner = owner;
}
return doDraw;
} /* gtk_draw_trayBegin */
static XP_Bool
gtkDrawTileImpl( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* textP,
const XP_Bitmaps* bitmaps, XP_U16 val, CellFlags flags,
XP_Bool clearBack )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
VDECL( XP_UCHAR, numbuf, 3 );
XP_Rect insetR = *rect;
XP_Bool isCursor = (flags & CELL_ISCURSOR) != 0;
XP_Bool valHidden = (flags & CELL_VALHIDDEN) != 0;
XP_Bool notEmpty = (flags & CELL_ISEMPTY) == 0;
if ( clearBack ) {
gtkEraseRect( dctx, &insetR );
}
if ( isCursor || notEmpty ) {
GdkRGBA* foreground = &dctx->playerColors[dctx->trayOwner];
XP_Rect formatRect = insetR;
gtkInsetRect( &insetR, 1 );
if ( clearBack ) {
gtkFillRect( dctx, &insetR,
isCursor ? &dctx->cursor : &dctx->tileBack );
}
formatRect.left += 3;
formatRect.width -= 6;
if ( notEmpty ) {
if ( !!bitmaps ) {
drawBitmapFromLBS( dctx, bitmaps->bmps[1], &insetR );
} else if ( !!textP ) {
if ( *textP != LETTER_NONE ) { /* blank */
draw_string_at( dctx, NULL, textP, formatRect.height>>1,
&formatRect, XP_GTK_JUST_TOPLEFT,
foreground, NULL );
}
}
if ( !valHidden ) {
XP_SNPRINTF( numbuf, VSIZE(numbuf), "%d", val );
draw_string_at( dctx, NULL, numbuf, formatRect.height>>2,
&formatRect, XP_GTK_JUST_BOTTOMRIGHT,
foreground, NULL );
}
}
/* frame the tile */
gtkSetForeground( dctx, &dctx->black );
draw_rectangle( dctx, FALSE,
insetR.left, insetR.top, insetR.width,
insetR.height );
if ( (flags & CELL_HIGHLIGHT) != 0 ) {
gtkInsetRect( &insetR, 1 );
draw_rectangle( dctx, FALSE, insetR.left, insetR.top,
insetR.width, insetR.height);
}
}
return XP_TRUE;
} /* gtkDrawTileImpl */
static XP_Bool
gtk_draw_drawTile( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* textP,
const XP_Bitmaps* bitmaps, XP_U16 val, CellFlags flags )
{
return gtkDrawTileImpl( p_dctx, rect, textP, bitmaps, val, flags, XP_TRUE );
}
#ifdef POINTER_SUPPORT
static XP_Bool
gtk_draw_drawTileMidDrag( DrawCtx* p_dctx, const XP_Rect* rect,
const XP_UCHAR* textP, const XP_Bitmaps* bitmaps,
XP_U16 val, XP_U16 owner, CellFlags flags )
{
gtk_draw_trayBegin( p_dctx, rect, owner, 0, DFS_NONE );
return gtkDrawTileImpl( p_dctx, rect, textP, bitmaps, val,
flags | CELL_HIGHLIGHT, XP_FALSE );
}
#endif
static XP_Bool
gtk_draw_drawTileBack( DrawCtx* p_dctx, const XP_Rect* rect,
CellFlags flags )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Bool hasCursor = (flags & CELL_ISCURSOR) != 0;
XP_Rect r = *rect;
gtkInsetRect( &r, 1 );
gtkFillRect( dctx, &r, &dctx->playerColors[dctx->trayOwner] );
gtkInsetRect( &r, 1 );
gtkFillRect( dctx, &r, hasCursor? &dctx->cursor : &dctx->tileBack );
draw_string_at( dctx, NULL, "?", r.height,
&r, XP_GTK_JUST_CENTER,
&dctx->playerColors[dctx->trayOwner], NULL );
return XP_TRUE;
} /* gtk_draw_drawTileBack */
static void
gtk_draw_drawTrayDivider( DrawCtx* p_dctx, const XP_Rect* rect,
CellFlags flags )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect r = *rect;
XP_Bool selected = 0 != (flags & CELL_HIGHLIGHT);
XP_Bool isCursor = 0 != (flags & CELL_ISCURSOR);
gtkEraseRect( dctx, &r );
gtkFillRect( dctx, &r, isCursor? &dctx->cursor:&dctx->white );
r.left += 2;
r.width -= 4;
if ( selected ) {
--r.height;
}
gtkSetForeground( dctx, &dctx->black );
draw_rectangle( dctx, !selected,
r.left, r.top, r.width, r.height);
} /* gtk_draw_drawTrayDivider */
static void
gtk_draw_clearRect( DrawCtx* p_dctx, const XP_Rect* rectP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect rect = *rectP;
++rect.width;
++rect.top;
gtkEraseRect( dctx, &rect );
} /* gtk_draw_clearRect */
static void
gtk_draw_drawBoardArrow( DrawCtx* p_dctx, const XP_Rect* rectP,
XWBonusType XP_UNUSED(cursorBonus), XP_Bool vertical,
HintAtts hintAtts, CellFlags XP_UNUSED(flags) )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
const XP_UCHAR* curs = vertical? "|":"-";
/* font needs to be small enough that "|" doesn't overwrite cell below */
draw_string_at( dctx, NULL, curs, (rectP->height*2)/3,
rectP, XP_GTK_JUST_CENTER,
&dctx->black, NULL );
drawHintBorders( dctx, rectP, hintAtts );
} /* gtk_draw_drawBoardCursor */
static XP_Bool
gtk_draw_scoreBegin( DrawCtx* p_dctx, const XP_Rect* rect,
XP_U16 XP_UNUSED(numPlayers),
const XP_S16* const XP_UNUSED(scores),
XP_S16 XP_UNUSED(remCount),
DrawFocusState XP_UNUSED(dfs) )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Bool doDraw = !dctx->surface;
if ( doDraw ) {
gtkEraseRect( dctx, rect );
dctx->scoreIsVertical = rect->height > rect->width;
}
return doDraw;
} /* gtk_draw_scoreBegin */
static PangoLayout*
getLayoutToFitRect( GtkDrawCtx* dctx, const XP_UCHAR* str, const XP_Rect* rect,
int* heightP )
{
PangoLayout* layout;
float ratio, ratioH;
int width, height;
XP_U16 len = XP_STRLEN(str);
/* First measure it using any font at all */
layout = layout_for_ht( dctx, 24 );
pango_layout_set_text( layout, (char*)str, len );
pango_layout_get_pixel_size( layout, &width, &height );
g_object_unref( layout );
/* Figure the ratio of is to should-be. The smaller of these is the one
we must use. */
ratio = (float)rect->width / (float)width;
ratioH = (float)rect->height / (float)height;
if ( ratioH < ratio ) {
ratio = ratioH;
}
height = 24.0 * ratio;
if ( !!heightP && *heightP < height ) {
height = *heightP;
}
layout = layout_for_ht( dctx, height );
pango_layout_set_text( layout, (char*)str, len );
if ( !!heightP ) {
*heightP = height;
}
return layout;
} /* getLayoutToFitRect */
static void
gtkDrawDrawRemText( DrawCtx* p_dctx, const XP_Rect* rect, XP_S16 nTilesLeft,
XP_U16* widthP, XP_U16* heightP, XP_Bool focussed )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_UCHAR buf[10];
XP_SNPRINTF( buf, sizeof(buf), "rem:%d", nTilesLeft );
PangoLayout* layout = getLayoutToFitRect( dctx, buf, rect, NULL );
if ( !!widthP ) {
int width, height;
pango_layout_get_pixel_size( layout, &width, &height );
if ( width > rect->width ) {
width = rect->width;
}
if ( height > rect->height ) {
height = rect->height;
}
*widthP = width;
*heightP = height;
} else {
const GdkRGBA* cursor = NULL;
if ( focussed ) {
cursor = &dctx->cursor;
gtkFillRect( dctx, rect, cursor );
}
draw_string_at( dctx, layout, buf, rect->height,
rect, XP_GTK_JUST_TOPLEFT,
&dctx->black, NULL );
}
g_object_unref( layout );
} /* gtkDrawDrawRemText */
static void
gtk_draw_score_drawPlayer( DrawCtx* p_dctx, const XP_Rect* rInner,
const XP_Rect* rOuter,
XP_U16 XP_UNUSED(gotPct), const DrawScoreInfo* dsi )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Bool hasCursor = (dsi->flags & CELL_ISCURSOR) != 0;
GdkRGBA* cursor = NULL;
XP_U16 playerNum = dsi->playerNum;
const XP_UCHAR* scoreBuf = dctx->scoreCache[playerNum].str;
XP_U16 fontHt = dctx->scoreCache[playerNum].fontHt;
if ( hasCursor ) {
cursor = &dctx->cursor;
gtkFillRect( dctx, rOuter, cursor );
}
gtkSetForeground( dctx, &dctx->playerColors[playerNum] );
if ( dsi->selected ) {
XP_Rect selRect = *rOuter;
XP_S16 diff;
if ( dctx->scoreIsVertical ) {
diff = selRect.height - rInner->height;
} else {
diff = selRect.width - rInner->width;
}
if ( diff > 0 ) {
if ( dctx->scoreIsVertical ) {
selRect.height -= diff>>1;
selRect.top += diff>>2;
} else {
selRect.width -= diff>>1;
selRect.left += diff>>2;
}
}
draw_rectangle( dctx, TRUE, selRect.left, selRect.top,
selRect.width, selRect.height );
if ( hasCursor ) {
gtkFillRect( dctx, rInner, cursor );
} else {
gtkEraseRect( dctx, rInner );
}
}
/* XP_U16 fontHt = rInner->height; */
/* if ( strstr( scoreBuf, "\n" ) ) { */
/* fontHt >>= 1; */
/* } */
draw_string_at( dctx, NULL, scoreBuf, fontHt/*-1*/,
rInner, XP_GTK_JUST_CENTER,
&dctx->playerColors[playerNum], cursor );
} /* gtk_draw_score_drawPlayer */
#ifdef XWFEATURE_SCOREONEPASS
static XP_Bool
gtk_draw_drawRemText( DrawCtx* p_dctx, XP_S16 nTilesLeft,
XP_Bool focussed, XP_Rect* rect )
{
XP_Bool drawIt = 0 <= nTilesLeft;
if ( drawIt ) {
XP_U16 width, height;
gtkDrawDrawRemText( p_dctx, rect, nTilesLeft, &width, &height, focussed );
rect->width = width;
rect->height = height;
gtkDrawDrawRemText( p_dctx, rect, nTilesLeft, NULL, NULL, focussed );
}
return drawIt;
}
static void
gtk_draw_score_drawPlayers( DrawCtx* p_dctx, const XP_Rect* scoreRect,
XP_U16 nPlayers,
DrawScoreInfo playerData[],
XP_Rect playerRects[] )
{
XP_USE( playerData );
XP_USE( p_dctx );
XP_U16 ii;
XP_Rect rect = *scoreRect;
rect.width /= nPlayers;
for ( ii = 0; ii < nPlayers; ++ii ) {
XP_U16 ignoreW, ignoreH;
XP_Rect innerR;
gtk_draw_measureScoreText( p_dctx, &rect, &playerData[ii], &ignoreW,
&ignoreH );
innerR = rect;
innerR.left += 4;
innerR.width -= 8;
gtk_draw_score_drawPlayer( p_dctx, &innerR, &rect, 0, &playerData[ii] );
playerRects[ii] = rect;
rect.left += rect.width;
}
}
#else
static XP_Bool
gtk_draw_measureRemText( DrawCtx* p_dctx, const XP_Rect* rect, XP_S16 nTilesLeft,
XP_U16* width, XP_U16* height )
{
XP_Bool drawIt = 0 <= nTilesLeft;
if ( drawIt ) {
gtkDrawDrawRemText( p_dctx, rect, nTilesLeft, width, height, XP_FALSE );
}
return drawIt;
} /* gtk_draw_measureRemText */
static void
gtk_draw_drawRemText( DrawCtx* p_dctx, const XP_Rect* rInner,
const XP_Rect* XP_UNUSED(rOuter), XP_S16 nTilesLeft,
XP_Bool focussed )
{
gtkDrawDrawRemText( p_dctx, rInner, nTilesLeft, NULL, NULL, focussed );
} /* gtk_draw_drawRemText */
#endif
static void
formatScoreText( PangoLayout* layout, XP_UCHAR* buf, XP_U16 bufLen,
const DrawScoreInfo* dsi, const XP_Rect* bounds,
XP_Bool scoreIsVertical, XP_U16* widthP, int* nLines )
{
XP_S16 score = dsi->totalScore;
XP_U16 nTilesLeft = dsi->nTilesLeft;
XP_Bool isTurn = dsi->isTurn;
XP_S16 maxWidth = bounds->width;
VDECL( XP_UCHAR, numBuf, 16 );
int width, height;
*nLines = 1;
XP_SNPRINTF( numBuf, VSIZE(numBuf), "%d", score );
if ( (nTilesLeft < MAX_TRAY_TILES) && (nTilesLeft > 0) ) {
VDECL( XP_UCHAR, tmp, 10 );
XP_SNPRINTF( tmp, VSIZE(tmp), ":%d", nTilesLeft );
(void)XP_STRCAT( numBuf, tmp );
}
if ( !!layout ) {
pango_layout_set_text( layout, (char*)numBuf, XP_STRLEN(numBuf) );
pango_layout_get_pixel_size( layout, &width, &height );
if ( !scoreIsVertical ) {
maxWidth -= width;
*widthP = width;
}
}
/* Reformat name + ':' until it fits */
VDECL( XP_UCHAR, name, MAX_SCORE_LEN ) = { 0 };
if ( isTurn && maxWidth > 0 ) {
XP_U16 len = 1 + XP_STRLEN( dsi->name ); /* +1 for "\0" */
if ( scoreIsVertical ) {
++*nLines;
} else {
++len; /* for ':' */
}
if ( len >= VSIZE(name) ) {
len = VSIZE(name) - 1;
}
for ( ; ; ) {
XP_SNPRINTF( name, len-1, "%s", dsi->name );
if ( !scoreIsVertical ) {
name[len-2] = ':';
name[len-1] = '\0';
}
pango_layout_set_text( layout, (char*)name, len );
pango_layout_get_pixel_size( layout, &width, &height );
if ( width <= maxWidth ) {
if ( !scoreIsVertical ) {
*widthP += width;
}
break;
}
if ( --len < 2 ) {
name[0] = '\0';
break;
}
}
}
if ( scoreIsVertical ) {
*widthP = bounds->width;
}
XP_SNPRINTF( buf, bufLen, "%s%s%s", name, (*nLines>1? XP_CR:""), numBuf );
} /* formatScoreText */
static void
gtk_draw_measureScoreText( DrawCtx* p_dctx, const XP_Rect* bounds,
const DrawScoreInfo* dsi,
XP_U16* widthP, XP_U16* heightP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
VDECL( XP_UCHAR, buf, 36 );
PangoLayout* layout;
int lineHeight = GTK_HOR_SCORE_HEIGHT, nLines;
layout = getLayoutToFitRect( dctx, "M", bounds, &lineHeight );
formatScoreText( layout, buf, VSIZE(buf), dsi, bounds,
dctx->scoreIsVertical, widthP, &nLines );
*heightP = nLines * lineHeight;
XP_U16 playerNum = dsi->playerNum;
XP_ASSERT( playerNum < MAX_NUM_PLAYERS );
XP_SNPRINTF( dctx->scoreCache[playerNum].str,
MAX_SCORE_LEN+1, "%s", buf );
dctx->scoreCache[playerNum].fontHt = lineHeight;
} /* gtk_draw_measureScoreText */
static void
gtk_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect,
XP_S16 score, XP_U16 playerNum,
XP_S16 curTurn, CellFlags flags )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
VDECL( XP_UCHAR, buf, 5 );
XP_U16 ht;
XP_Rect localR;
GdkRGBA* cursor = ((flags & CELL_ISCURSOR) != 0)
? &dctx->cursor : NULL;
GdkRGBA* txtColor;
if ( score >= 0 ) {
XP_SNPRINTF( buf, VSIZE(buf), "%.3d", score );
} else {
XP_STRNCPY( buf, "???", VSIZE(buf) );
}
/* gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)rect ); */
localR = *rect;
gtkInsetRect( &localR, 1 );
if ( !!cursor ) {
gtkFillRect( dctx, &localR, cursor );
} else {
gtkEraseRect( dctx, &localR );
}
ht = localR.height >> 2;
txtColor = (playerNum == curTurn) ? &dctx->black : &dctx->grey;
draw_string_at( dctx, NULL, "Pts:", ht,
&localR, XP_GTK_JUST_TOPLEFT, txtColor, cursor );
draw_string_at( dctx, NULL, buf, ht,
&localR, XP_GTK_JUST_BOTTOMRIGHT, txtColor, cursor );
} /* gtk_draw_score_pendingScore */
static void
gtkFormatTimerText( XP_UCHAR* buf, XP_U16 bufLen, XP_S16 secondsLeft )
{
XP_U16 minutes, seconds;
if ( secondsLeft < 0 ) {
*buf++ = '-';
--bufLen;
secondsLeft *= -1;
}
minutes = secondsLeft / 60;
seconds = secondsLeft % 60;
XP_SNPRINTF( buf, bufLen, "% 1d:%02d", minutes, seconds );
} /* gtkFormatTimerText */
static void
gtk_draw_drawTimer( DrawCtx* p_dctx, const XP_Rect* rInner,
XP_U16 playerNum, XP_S16 secondsLeft )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Bool hadCairo = haveCairo( dctx );
if ( hadCairo || initCairo( dctx ) ) {
VDECL( XP_UCHAR, buf, 10 );
gtkFormatTimerText( buf, VSIZE(buf), secondsLeft );
gtkEraseRect( dctx, rInner );
draw_string_at( dctx, NULL, buf, rInner->height-1,
rInner, XP_GTK_JUST_CENTER,
&dctx->playerColors[playerNum], NULL );
if ( !hadCairo ) {
destroyCairo( dctx );
}
}
} /* gtk_draw_drawTimer */
#ifdef XWFEATURE_MINIWIN
# define MINI_LINE_HT 12
# define MINI_V_PADDING 6
# define MINI_H_PADDING 8
static void
frameRect( GtkDrawCtx* dctx, const XP_Rect* rect )
{
draw_rectangle( dctx, DRAW_WHAT(dctx), dctx->drawGC,
FALSE, rect->left, rect->top,
rect->width, rect->height );
} /* frameRect */
static const XP_UCHAR*
gtk_draw_getMiniWText( DrawCtx* XP_UNUSED(p_dctx), XWMiniTextType textHint )
{
/* GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; */
XP_UCHAR* str;
switch( textHint ) {
case BONUS_DOUBLE_LETTER:
str = "Double letter"; break;
case BONUS_DOUBLE_WORD:
str = "Double word"; break;
case BONUS_TRIPLE_LETTER:
str = "Triple letter"; break;
case BONUS_TRIPLE_WORD:
str = "Triple word"; break;
case INTRADE_MW_TEXT:
str = "Trading tiles;\nclick D when done"; break;
default:
XP_ASSERT( XP_FALSE );
}
return str;
} /* gtk_draw_getMiniWText */
static void
gtk_draw_measureMiniWText( DrawCtx* p_dctx, const XP_UCHAR* str,
XP_U16* widthP, XP_U16* heightP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
int height, width;
PangoLayout* layout = layout_for_ht( dctx, GTKMIN_W_HT );
pango_layout_set_text( layout, (char*)str, XP_STRLEN(str) );
pango_layout_get_pixel_size( layout, &width, &height );
*heightP = height;
*widthP = width + 6;
} /* gtk_draw_measureMiniWText */
static void
gtk_draw_drawMiniWindow( DrawCtx* p_dctx, const XP_UCHAR* text,
const XP_Rect* rect, void** XP_UNUSED(closureP) )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect localR = *rect;
gtkSetForeground( dctx, &dctx->black );
/* play some skanky games to get the shadow drawn under and to the
right... */
gtkEraseRect( dctx, &localR );
gtkInsetRect( &localR, 1 );
--localR.width;
--localR.height;
frameRect( dctx, &localR );
--localR.top;
--localR.left;
gtkEraseRect( dctx, &localR );
frameRect( dctx, &localR );
draw_string_at( dctx, NULL, text, GTKMIN_W_HT,
&localR, XP_GTK_JUST_CENTER,
&dctx->black, NULL );
} /* gtk_draw_drawMiniWindow */
#endif
#define SET_GDK_COLOR( c, r, g, b ) { \
c.red = (r); \
c.green = (g); \
c.blue = (b); \
}
static void
draw_doNothing( DrawCtx* XP_UNUSED(dctx), ... )
{
} /* draw_doNothing */
static gdouble
figureColor( int in )
{
gdouble asDouble = (gdouble)in;
gdouble result = asDouble / 0xFFFF;
// XP_LOGF( "%s(%d): asDouble: %lf; result: %lf", __func__, in, asDouble, result );
XP_ASSERT( result >= 0 && result <= 1.0 );
return result;
}
static void
allocAndSet( GDKCOLORMAP* map, GdkRGBA* color, unsigned short red,
unsigned short green, unsigned short blue )
{
#ifdef USE_CAIRO
XP_USE( map );
color->red = figureColor(red);
color->green = figureColor(green);
color->blue = figureColor(blue);
color->alpha = 1.0;
#else
color->red = red;
color->green = green;
color->blue = blue;
#ifdef DEBUG
gboolean success =
#endif
gdk_colormap_alloc_color( map,
color,
TRUE, /* writeable */
TRUE ); /* best-match */
XP_ASSERT( success );
#endif
} /* allocAndSet */
DrawCtx*
gtkDrawCtxtMake( GtkWidget* drawing_area, GtkGameGlobals* globals )
{
GtkDrawCtx* dctx = g_malloc0( sizeof(*dctx) );
dctx->vtable = g_malloc( sizeof(*(((GtkDrawCtx*)dctx)->vtable)) );
void** ptr = (void**)dctx->vtable;
void** tableEnd = (void**)&dctx->vtable->tableEnd;
while ( ptr < tableEnd ) {
*ptr++ = draw_doNothing;
}
SET_VTABLE_ENTRY( dctx->vtable, draw_clearRect, gtk );
#ifdef DRAW_WITH_PRIMITIVES
SET_VTABLE_ENTRY( dctx->vtable, draw_setClip, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_frameRect, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_invertRect, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawString, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawBitmap, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureText, gtk_prim );
InitDrawDefaults( dctx->vtable );
#else
SET_VTABLE_ENTRY( dctx->vtable, draw_beginDraw, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_endDraw, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_boardBegin, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawCell, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_invertCell, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_objFinished, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_vertScrollBoard, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_trayBegin, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTile, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTileBack, gtk );
#ifdef POINTER_SUPPORT
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTileMidDrag, gtk );
#endif
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTrayDivider, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawBoardArrow, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_scoreBegin, gtk );
#ifdef XWFEATURE_SCOREONEPASS
SET_VTABLE_ENTRY( dctx->vtable, draw_drawRemText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_score_drawPlayers, gtk );
#else
SET_VTABLE_ENTRY( dctx->vtable, draw_measureRemText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawRemText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureScoreText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_score_drawPlayer, gtk );
#endif
SET_VTABLE_ENTRY( dctx->vtable, draw_score_pendingScore, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTimer, gtk );
#ifdef XWFEATURE_MINIWIN
SET_VTABLE_ENTRY( dctx->vtable, draw_getMiniWText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureMiniWText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawMiniWindow, gtk );
#endif
SET_VTABLE_ENTRY( dctx->vtable, draw_destroyCtxt, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_dictChanged, gtk );
#endif
dctx->drawing_area = drawing_area;
dctx->globals = globals;
XP_ASSERT( !!gtk_widget_get_window(drawing_area) );
#ifdef USE_CAIRO
/* dctx->cairo = gdk_cairo_create( window ); */
/* XP_LOGF( "dctx->cairo=%p", dctx->cairo ); */
/* cairo_set_line_width( dctx->cairo, 1.0 ); */
/* cairo_set_line_cap( dctx->cairo, CAIRO_LINE_CAP_SQUARE ); */
/* cairo_set_source_rgb( dctx->cairo, 0, 0, 0 ); */
#else
dctx->drawGC = gdk_gc_new( window );
#endif
GDKCOLORMAP* map = NULL;
#ifndef USE_CAIRO
map = gdk_colormap_get_system();
#endif
allocAndSet( map, &dctx->black, 0x0000, 0x0000, 0x0000 );
allocAndSet( map, &dctx->grey, 0x7FFF, 0x7FFF, 0x7FFF );
allocAndSet( map, &dctx->white, 0xFFFF, 0xFFFF, 0xFFFF );
allocAndSet( map, &dctx->bonusColors[0], 0xFFFF, 0xAFFF, 0xAFFF );
allocAndSet( map, &dctx->bonusColors[1], 0xAFFF, 0xFFFF, 0xAFFF );
allocAndSet( map, &dctx->bonusColors[2], 0xAFFF, 0xAFFF, 0xFFFF );
allocAndSet( map, &dctx->bonusColors[3], 0xFFFF, 0xAFFF, 0xFFFF );
allocAndSet( map, &dctx->playerColors[0], 0x0000, 0x0000, 0xAFFF );
allocAndSet( map, &dctx->playerColors[1], 0xAFFF, 0x0000, 0x0000 );
allocAndSet( map, &dctx->playerColors[2], 0x0000, 0xAFFF, 0x0000 );
allocAndSet( map, &dctx->playerColors[3], 0xAFFF, 0x0000, 0xAFFF );
allocAndSet( map, &dctx->tileBack, 0xFFFF, 0xFFFF, 0x9999 );
allocAndSet( map, &dctx->cursor, 253<<8, 12<<8, 222<<8 );
allocAndSet( map, &dctx->red, 0xFFFF, 0x0000, 0x0000 );
return (DrawCtx*)dctx;
} /* gtkDrawCtxtMake */
void
addSurface( GtkDrawCtx* dctx, int width, int height )
{
XP_ASSERT( !dctx->surface );
dctx->surface = cairo_image_surface_create( CAIRO_FORMAT_RGB24, width, height );
XP_ASSERT( !!dctx->surface );
}
void
removeSurface( GtkDrawCtx* dctx )
{
XP_ASSERT( !!dctx->surface );
cairo_surface_destroy( dctx->surface );
dctx->surface = NULL;
}
#ifdef DEBUG
static cairo_status_t
write_func( void *closure, const unsigned char *data,
unsigned int length )
{
XWStreamCtxt* stream = (XWStreamCtxt*)closure;
stream_putBytes( stream, data, length );
return CAIRO_STATUS_SUCCESS;
}
#endif
void
getImage( GtkDrawCtx* XP_UNUSED_DBG(dctx), XWStreamCtxt* XP_UNUSED_DBG(stream) )
{
LOG_FUNC();
XP_ASSERT( !!dctx->surface );
#ifdef DEBUG
cairo_status_t status =
cairo_surface_write_to_png_stream( dctx->surface,
write_func, stream );
XP_ASSERT( CAIRO_STATUS_SUCCESS == status );
#else
error Will Robinson
#endif
}
void
draw_gtk_status( GtkDrawCtx* dctx, char ch )
{
if ( initCairo( dctx ) ) {
GtkGameGlobals* globals = dctx->globals;
XP_Rect rect = {
.left = globals->netStatLeft,
.top = globals->netStatTop,
.width = GTK_NETSTAT_WIDTH,
.height = GTK_HOR_SCORE_HEIGHT
};
gtkEraseRect( dctx, &rect );
const XP_UCHAR str[2] = { ch, '\0' };
draw_string_at( dctx, NULL, str, GTKMIN_W_HT,
&rect, XP_GTK_JUST_CENTER,
&dctx->black, NULL );
destroyCairo( dctx );
}
}
void
frame_active_rect( GtkDrawCtx* dctx, const XP_Rect* rect )
{
gtkFillRect( dctx, rect, &dctx->grey );
}
#endif /* PLATFORM_GTK */