/* $XdotOrg: lib/Xfont/src/fc/fserve.c,v 1.8 2005/07/09 06:36:12 keithp Exp $ */
/* $Xorg: fserve.c,v 1.4 2001/02/09 02:04:02 xorgcvs Exp $ */
/*
Copyright 1990, 1998 The Open Group
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.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of The Open Group shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from The Open Group.
*/
/* $XFree86: xc/lib/font/fc/fserve.c,v 3.26tsi Exp $ */
/*
* Copyright 1990 Network Computing Devices
*
* 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 names of Network Computing Devices, or Digital
* not be used in advertising or publicity pertaining to distribution
* of the software without specific, written prior permission.
*
* NETWORK COMPUTING DEVICES, AND DIGITAL AND DISCLAIM ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL NETWORK COMPUTING DEVICES,
* OR DIGITAL 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: Dave Lemke, Network Computing Devices, Inc
*/
/*
* font server specific font access
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#define _WILLWINSOCK_
#endif
#define FONT_t
#define TRANS_CLIENT
#include "X11/Xtrans/Xtrans.h"
#include "X11/Xpoll.h"
#include <X11/fonts/FS.h>
#include <X11/fonts/FSproto.h>
#include <X11/X.h>
#include <X11/Xos.h>
#include <X11/fonts/fontmisc.h>
#include <X11/fonts/fontstruct.h>
#include "fservestr.h"
#include <X11/fonts/fontutil.h>
#include <errno.h>
#include <time.h>
#define Time_t time_t
#ifdef NCD
#include <ncd/nvram.h>
#endif
#include <stddef.h>
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#define TimeCmp(a,c,b) ((int) ((a) - (b)) c 0)
#define NONZEROMETRICS(pci) ((pci)->leftSideBearing || \
(pci)->rightSideBearing || \
(pci)->ascent || \
(pci)->descent || \
(pci)->characterWidth)
extern void ErrorF(const char *f, ...);
static int fs_read_glyphs ( FontPathElementPtr fpe, FSBlockDataPtr blockrec );
static int fs_read_list ( FontPathElementPtr fpe, FSBlockDataPtr blockrec );
static int fs_read_list_info ( FontPathElementPtr fpe,
FSBlockDataPtr blockrec );
extern fd_set _fs_fd_mask;
static void fs_block_handler ( pointer data, OSTimePtr wt,
pointer LastSelectMask );
static int fs_wakeup ( FontPathElementPtr fpe, unsigned long *mask );
/*
* List of all FPEs
*/
static FSFpePtr fs_fpes;
/*
* Union of all FPE blockStates
*/
static CARD32 fs_blockState;
static int _fs_restart_connection ( FSFpePtr conn );
static void fs_send_query_bitmaps ( FontPathElementPtr fpe,
FSBlockDataPtr blockrec );
static int fs_send_close_font ( FontPathElementPtr fpe, Font id );
static void fs_client_died ( pointer client, FontPathElementPtr fpe );
static void _fs_client_access ( FSFpePtr conn, pointer client, Bool sync );
static void _fs_client_resolution ( FSFpePtr conn );
static fsGenericReply *fs_get_reply (FSFpePtr conn, int *error);
static int fs_await_reply (FSFpePtr conn);
static void _fs_do_blocked (FSFpePtr conn);
static void fs_cleanup_bfont (FSBlockedFontPtr bfont);
char _fs_glyph_undefined;
char _fs_glyph_requested;
static char _fs_glyph_zero_length;
static int generationCount;
static int FontServerRequestTimeout = 30 * 1000;
static void
_fs_close_server (FSFpePtr conn);
static FSFpePtr
_fs_init_conn (char *servername);
static int
_fs_wait_connect (FSFpePtr conn);
static int
_fs_send_init_packets (FSFpePtr conn);
static void
_fs_check_reconnect (FSFpePtr conn);
static void
_fs_start_reconnect (FSFpePtr conn);
static void
_fs_free_conn (FSFpePtr conn);
static int
fs_free_fpe(FontPathElementPtr fpe);
/*
* Font server access
*
* the basic idea for the non-blocking access is to have the function
* called multiple times until the actual data is returned, instead
* of ClientBlocked.
*
* the first call to the function will cause the request to be sent to
* the font server, and a block record to be stored in the fpe's list
* of outstanding requests. the FS block handler also sticks the
* proper set of fd's into the select mask. when data is ready to be
* read in, the FS wakup handler will be hit. this will read the
* data off the wire into the proper block record, and then signal the
* client that caused the block so that it can restart. it will then
* call the access function again, which will realize that the data has
* arrived and return it.
*/
#ifdef DEBUG
static void
_fs_add_req_log(FSFpePtr conn, int opcode)
{
conn->current_seq++;
fprintf (stderr, "\t\tRequest: %5d Opcode: %2d\n",
conn->current_seq, opcode);
conn->reqbuffer[conn->reqindex].opcode = opcode;
conn->reqbuffer[conn->reqindex].sequence = conn->current_seq;
conn->reqindex++;
if (conn->reqindex == REQUEST_LOG_SIZE)
conn->reqindex = 0;
}
static void
_fs_add_rep_log (FSFpePtr conn, fsGenericReply *rep)
{
int i;
for (i = 0; i < REQUEST_LOG_SIZE; i++)
if (conn->reqbuffer[i].sequence == rep->sequenceNumber)
break;
if (i == REQUEST_LOG_SIZE)
fprintf (stderr, "\t\t\t\t\tReply: %5d Opcode: unknown\n",
rep->sequenceNumber);
else
fprintf (stderr, "\t\t\t\t\tReply: %5d Opcode: %d\n",
rep->sequenceNumber,
conn->reqbuffer[i].opcode);
}
#else
#define _fs_add_req_log(conn,op) ((conn)->current_seq++)
#define _fs_add_rep_log(conn,rep)
#endif
static Bool
fs_name_check(char *name)
{
#ifdef __UNIXOS2__
/* OS/2 uses D:/usr/X11R6/.... as fontfile pathnames, so check that
* there is not only a protocol/ prefix, but also that the first chars
* are not a drive letter
*/
if (name && isalpha(*name) && name[1] == ':')
return FALSE;
#endif
/* Just make sure there is a protocol/ prefix */
return (name && *name != '/' && strchr(name, '/'));
}
static void
_fs_client_resolution(FSFpePtr conn)
{
fsSetResolutionReq srreq;
int num_res;
FontResolutionPtr res;
res = GetClientResolutions(&num_res);
if (num_res) {
srreq.reqType = FS_SetResolution;
srreq.num_resolutions = num_res;
srreq.length = (SIZEOF(fsSetResolutionReq) +
(num_res * SIZEOF(fsResolution)) + 3) >> 2;
_fs_add_req_log(conn, FS_SetResolution);
if (_fs_write(conn, (char *) &srreq, SIZEOF(fsSetResolutionReq)) != -1)
(void)_fs_write_pad(conn, (char *) res,
(num_res * SIZEOF(fsResolution)));
}
}
/*
* close font server and remove any state associated with
* this connection - this includes any client records.
*/
static void
fs_close_conn(FSFpePtr conn)
{
FSClientPtr client, nclient;
_fs_close_server (conn);
for (client = conn->clients; client; client = nclient)
{
nclient = client->next;
xfree (client);
}
conn->clients = NULL;
}
/*
* the wakeup handlers have to be set when the FPE is open, and not
* removed until it is freed, in order to handle unexpected data, like
* events
*/
/* ARGSUSED */
static int
fs_init_fpe(FontPathElementPtr fpe)
{
FSFpePtr conn;
char *name;
int err;
int ret;
/* open font server */
/* create FS specific fpe info */
name = fpe->name;
/* hack for old style names */
if (*name == ':')
name++; /* skip ':' */
conn = _fs_init_conn (name);
if (!conn)
err = AllocError;
else
{
err = init_fs_handlers (fpe, fs_block_handler);
if (err != Successful)
{
_fs_free_conn (conn);
err = AllocError;
}
else
{
fpe->private = conn;
conn->next = fs_fpes;
fs_fpes = conn;
ret = _fs_wait_connect (conn);
if (ret != FSIO_READY)
{
fs_free_fpe (fpe);
err = BadFontPath;
}
else
err = Successful;
}
}
if (err == Successful)
{
#ifdef NCD
if (configData.ExtendedFontDiags)
printf("Connected to font server \"%s\"\n", name);
#endif
#ifdef DEBUG
fprintf (stderr, "connected to FS \"%s\"\n", name);
#endif
}
else
{
#ifdef DEBUG
fprintf(stderr, "failed to connect to FS \"%s\" %d\n", name, err);
#endif
#ifdef NCD
if (configData.ExtendedFontDiags)
printf("Failed to connect to font server \"%s\"\n", name);
#endif
;
}
return err;
}
static int
fs_reset_fpe(FontPathElementPtr fpe)
{
(void) _fs_send_init_packets((FSFpePtr) fpe->private);
return Successful;
}
/*
* this shouldn't be called till all refs to the FPE are gone
*/
static int
fs_free_fpe(FontPathElementPtr fpe)
{
FSFpePtr conn = (FSFpePtr) fpe->private, *prev;
/* unhook from chain of all font servers */
for (prev = &fs_fpes; *prev; prev = &(*prev)->next)
{
if (*prev == conn)
{
*prev = conn->next;
break;
}
}
_fs_unmark_block (conn, conn->blockState);
fs_close_conn(conn);
remove_fs_handlers(fpe, fs_block_handler, fs_fpes == 0);
_fs_free_conn (conn);
fpe->private = (pointer) 0;
#ifdef NCD
if (configData.ExtendedFontDiags)
printf("Disconnected from font server \"%s\"\n", fpe->name);
#endif
#ifdef DEBUG
fprintf (stderr, "disconnect from FS \"%s\"\n", fpe->name);
#endif
return Successful;
}
static FSBlockDataPtr
fs_new_block_rec(FontPathElementPtr fpe, pointer client, int type)
{
FSBlockDataPtr blockrec,
*prev;
FSFpePtr conn = (FSFpePtr) fpe->private;
int size;
switch (type) {
case FS_OPEN_FONT:
size = sizeof(FSBlockedFontRec);
break;
case FS_LOAD_GLYPHS:
size = sizeof(FSBlockedGlyphRec);
break;
case FS_LIST_FONTS:
size = sizeof(FSBlockedListRec);
break;
case FS_LIST_WITH_INFO:
size = sizeof(FSBlockedListInfoRec);
break;
default:
size = 0;
break;
}
blockrec = (FSBlockDataPtr) xalloc(sizeof(FSBlockDataRec) + size);
if (!blockrec)
return (FSBlockDataPtr) 0;
blockrec->data = (pointer) (blockrec + 1);
blockrec->client = client;
blockrec->sequenceNumber = -1;
blockrec->errcode = StillWorking;
blockrec->type = type;
blockrec->depending = 0;
blockrec->next = (FSBlockDataPtr) 0;
/* stick it on the end of the list (since its expected last) */
for (prev = &conn->blockedRequests; *prev; prev = &(*prev)->next)
;
*prev = blockrec;
return blockrec;
}
static void
_fs_set_pending_reply (FSFpePtr conn)
{
FSBlockDataPtr blockrec;
for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
if (blockrec->errcode == StillWorking)
break;
if (blockrec)
{
conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
_fs_mark_block (conn, FS_PENDING_REPLY);
}
else
_fs_unmark_block (conn, FS_PENDING_REPLY);
}
static void
_fs_remove_block_rec(FSFpePtr conn, FSBlockDataPtr blockrec)
{
FSBlockDataPtr *prev;
for (prev = &conn->blockedRequests; *prev; prev = &(*prev)->next)
if (*prev == blockrec)
{
*prev = blockrec->next;
break;
}
if (blockrec->type == FS_LOAD_GLYPHS)
{
FSBlockedGlyphPtr bglyph = (FSBlockedGlyphPtr)blockrec->data;
if (bglyph->num_expected_ranges)
xfree(bglyph->expected_ranges);
}
xfree(blockrec);
_fs_set_pending_reply (conn);
}
static void
_fs_signal_clients_depending(FSClientsDependingPtr *clients_depending)
{
FSClientsDependingPtr p;
while ((p = *clients_depending))
{
*clients_depending = p->next;
ClientSignal(p->client);
xfree(p);
}
}
static int
_fs_add_clients_depending(FSClientsDependingPtr *clients_depending, pointer client)
{
FSClientsDependingPtr new, cd;
for (; (cd = *clients_depending);
clients_depending = &(*clients_depending)->next)
{
if (cd->client == client)
return Suspended;
}
new = (FSClientsDependingPtr)xalloc (sizeof (FSClientsDependingRec));
if (!new)
return BadAlloc;
new->client = client;
new->next = 0;
*clients_depending = new;
return Suspended;
}
/*
* When a request is aborted due to a font server failure,
* signal any depending clients to restart their dependant
* requests
*/
static void
_fs_clean_aborted_blockrec(FSFpePtr conn, FSBlockDataPtr blockrec)
{
switch(blockrec->type) {
case FS_OPEN_FONT: {
FSBlockedFontPtr bfont = (FSBlockedFontPtr)blockrec->data;
fs_cleanup_bfont (bfont);
_fs_signal_clients_depending(&bfont->clients_depending);
break;
}
case FS_LOAD_GLYPHS: {
FSBlockedGlyphPtr bglyph = (FSBlockedGlyphPtr)blockrec->data;
_fs_clean_aborted_loadglyphs(bglyph->pfont,
bglyph->num_expected_ranges,
bglyph->expected_ranges);
_fs_signal_clients_depending(&bglyph->clients_depending);
break;
}
case FS_LIST_FONTS:
break;
case FS_LIST_WITH_INFO: {
FSBlockedListInfoPtr binfo;
binfo = (FSBlockedListInfoPtr) blockrec->data;
if (binfo->status == FS_LFWI_REPLY)
FD_SET(conn->fs_fd, &_fs_fd_mask);
_fs_free_props (&binfo->info);
}
default:
break;
}
}
static void
fs_abort_blockrec(FSFpePtr conn, FSBlockDataPtr blockrec)
{
_fs_clean_aborted_blockrec (conn, blockrec);
_fs_remove_block_rec (conn, blockrec);
}
/*
* Tell the font server we've failed to complete an open and
* then unload the partially created font
*/
static void
fs_cleanup_bfont (FSBlockedFontPtr bfont)
{
FSFontDataRec *fsd;
if (bfont->pfont)
{
fsd = (FSFontDataRec *) bfont->pfont->fpePrivate;
/* make sure the FS knows we choked on it */
fs_send_close_font(bfont->pfont->fpe, bfont->fontid);
/*
* Either unload the font if it's being opened for
* the first time, or smash the generation field to
* mark this font as an orphan
*/
if (!(bfont->flags & FontReopen))
{
if (bfont->freeFont)
(*bfont->pfont->unload_font) (bfont->pfont);
#ifdef DEBUG
else
fprintf (stderr, "Not freeing other font in cleanup_bfont\n");
#endif
bfont->pfont = 0;
}
else
fsd->generation = -1;
}
}
/*
* Check to see if a complete reply is waiting
*/
static fsGenericReply *
fs_get_reply (FSFpePtr conn, int *error)
{
char *buf;
fsGenericReply *rep;
int ret;
/* block if the connection is down or paused in lfwi */
if (conn->fs_fd == -1 || !FD_ISSET (conn->fs_fd, &_fs_fd_mask))
{
*error = FSIO_BLOCK;
return 0;
}
ret = _fs_start_read (conn, sizeof (fsGenericReply), &buf);
if (ret != FSIO_READY)
{
*error = FSIO_BLOCK;
return 0;
}
rep = (fsGenericReply *) buf;
ret = _fs_start_read (conn, rep->length << 2, &buf);
if (ret != FSIO_READY)
{
*error = FSIO_BLOCK;
return 0;
}
*error = FSIO_READY;
return (fsGenericReply *) buf;
}
static Bool
fs_reply_ready (FSFpePtr conn)
{
fsGenericReply *rep;
if (conn->fs_fd == -1 || !FD_ISSET (conn->fs_fd, &_fs_fd_mask))
return FALSE;
if (fs_data_read (conn) < sizeof (fsGenericReply))
return FALSE;
rep = (fsGenericReply *) (conn->inBuf.buf + conn->inBuf.remove);
if (fs_data_read (conn) < rep->length << 2)
return FALSE;
return TRUE;
}
static void
_fs_pending_reply (FSFpePtr conn)
{
if (!(conn->blockState & FS_PENDING_REPLY))
{
_fs_mark_block (conn, FS_PENDING_REPLY);
conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
}
}
static void
_fs_prepare_for_reply (FSFpePtr conn)
{
_fs_pending_reply (conn);
_fs_flush (conn);
}
/*
* Block (for a while) awaiting a complete reply
*/
static int
fs_await_reply (FSFpePtr conn)
{
int ret;
if (conn->blockState & FS_COMPLETE_REPLY)
return FSIO_READY;
while (!fs_get_reply (conn, &ret))
{
if (ret != FSIO_BLOCK)
return ret;
if (_fs_wait_for_readable (conn, FontServerRequestTimeout) != FSIO_READY)
{
_fs_connection_died (conn);
return FSIO_ERROR;
}
}
return FSIO_READY;
}
/*
* Process the reply to an OpenBitmapFont request
*/
static int
fs_read_open_font(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
FSBlockedFontPtr bfont = (FSBlockedFontPtr) blockrec->data;
fsOpenBitmapFontReply *rep;
FSBlockDataPtr blockOrig;
FSBlockedFontPtr origBfont;
int ret;
rep = (fsOpenBitmapFontReply *) fs_get_reply (conn, &ret);
if (!rep || rep->type == FS_Error)
{
if (ret == FSIO_BLOCK)
return StillWorking;
if (rep)
_fs_done_read (conn, rep->length << 2);
fs_cleanup_bfont (bfont);
return BadFontName;
}
/* If we're not reopening a font and FS detected a duplicate font
open request, replace our reference to the new font with a
reference to an existing font (possibly one not finished
opening). If this is a reopen, keep the new font reference...
it's got the metrics and extents we read when the font was opened
before. This also gives us the freedom to easily close the font
if we we decide (in fs_read_query_info()) that we don't like what
we got. */
if (rep->otherid && !(bfont->flags & FontReopen))
{
fs_cleanup_bfont (bfont);
/* Find old font if we're completely done getting it from server. */
bfont->pfont = find_old_font(rep->otherid);
bfont->freeFont = FALSE;
bfont->fontid = rep->otherid;
bfont->state = FS_DONE_REPLY;
/*
* look for a blocked request to open the same font
*/
for (blockOrig = conn->blockedRequests;
blockOrig;
blockOrig = blockOrig->next)
{
if (blockOrig != blockrec && blockOrig->type == FS_OPEN_FONT)
{
origBfont = (FSBlockedFontPtr) blockOrig->data;
if (origBfont->fontid == rep->otherid)
{
blockrec->depending = blockOrig->depending;
blockOrig->depending = blockrec;
bfont->state = FS_DEPENDING;
bfont->pfont = origBfont->pfont;
break;
}
}
}
if (bfont->pfont == NULL)
{
/* XXX - something nasty happened */
ret = BadFontName;
}
else
ret = AccessDone;
}
else
{
bfont->pfont->info.cachable = rep->cachable != 0;
bfont->state = FS_INFO_REPLY;
/*
* Reset the blockrec for the next reply
*/
blockrec->sequenceNumber = bfont->queryInfoSequence;
conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
ret = StillWorking;
}
_fs_done_read (conn, rep->length << 2);
return ret;
}
static Bool
fs_fonts_match (FontInfoPtr pInfo1, FontInfoPtr pInfo2)
{
int i;
if (pInfo1->firstCol != pInfo2->firstCol ||
pInfo1->lastCol != pInfo2->lastCol ||
pInfo1->firstRow != pInfo2->firstRow ||
pInfo1->lastRow != pInfo2->lastRow ||
pInfo1->defaultCh != pInfo2->defaultCh ||
pInfo1->noOverlap != pInfo2->noOverlap ||
pInfo1->terminalFont != pInfo2->terminalFont ||
pInfo1->constantMetrics != pInfo2->constantMetrics ||
pInfo1->constantWidth != pInfo2->constantWidth ||
pInfo1->inkInside != pInfo2->inkInside ||
pInfo1->inkMetrics != pInfo2->inkMetrics ||
pInfo1->allExist != pInfo2->allExist ||
pInfo1->drawDirection != pInfo2->drawDirection ||
pInfo1->cachable != pInfo2->cachable ||
pInfo1->anamorphic != pInfo2->anamorphic ||
pInfo1->maxOverlap != pInfo2->maxOverlap ||
pInfo1->fontAscent != pInfo2->fontAscent ||
pInfo1->fontDescent != pInfo2->fontDescent ||
pInfo1->nprops != pInfo2->nprops)
return FALSE;
#define MATCH(xci1, xci2) \
(((xci1).leftSideBearing == (xci2).leftSideBearing) && \
((xci1).rightSideBearing == (xci2).rightSideBearing) && \
((xci1).characterWidth == (xci2).characterWidth) && \
((xci1).ascent == (xci2).ascent) && \
((xci1).descent == (xci2).descent) && \
((xci1).attributes == (xci2).attributes))
if (!MATCH(pInfo1->maxbounds, pInfo2->maxbounds) ||
!MATCH(pInfo1->minbounds, pInfo2->minbounds) ||
!MATCH(pInfo1->ink_maxbounds, pInfo2->ink_maxbounds) ||
!MATCH(pInfo1->ink_minbounds, pInfo2->ink_minbounds))
return FALSE;
#undef MATCH
for (i = 0; i < pInfo1->nprops; i++)
if (pInfo1->isStringProp[i] !=
pInfo2->isStringProp[i] ||
pInfo1->props[i].name !=
pInfo2->props[i].name ||
pInfo1->props[i].value !=
pInfo2->props[i].value)
{
return FALSE;
}
return TRUE;
}
static int
fs_read_query_info(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
{
FSBlockedFontPtr bfont = (FSBlockedFontPtr) blockrec->data;
FSFpePtr conn = (FSFpePtr) fpe->private;
fsQueryXInfoReply *rep;
char *buf;
fsPropInfo *pi;
fsPropOffset *po;
pointer pd;
FontInfoPtr pInfo;
FontInfoRec tempInfo;
int err;
int ret;
rep = (fsQueryXInfoReply *) fs_get_reply (conn, &ret);
if (!rep || rep->type == FS_Error)
{
if (ret == FSIO_BLOCK)
return StillWorking;
if (rep)
_fs_done_read (conn, rep->length << 2);
fs_cleanup_bfont (bfont);
return BadFontName;
}
/* If this is a reopen, accumulate the query info into a dummy
font and compare to our original data. */
if (bfont->flags & FontReopen)
pInfo = &tempInfo;
else
pInfo = &bfont->pfont->info;
buf = (char *) rep;
buf += SIZEOF(fsQueryXInfoReply);
/* move the data over */
fsUnpack_XFontInfoHeader(rep, pInfo);
/* compute accelerators */
_fs_init_fontinfo(conn, pInfo);
/* Compute offsets into the reply */
pi = (fsPropInfo *) buf;
buf += SIZEOF (fsPropInfo);
po = (fsPropOffset *) buf;
buf += pi->num_offsets * SIZEOF(fsPropOffset);
pd = (pointer) buf;
buf += pi->data_len;
/* convert the properties and step over the reply */
ret = _fs_convert_props(pi, po, pd, pInfo);
_fs_done_read (conn, rep->length << 2);
if (ret == -1)
{
fs_cleanup_bfont (bfont);
return AllocError;
}
if (bfont->flags & FontReopen)
{
/* We're reopening a font that we lost because of a downed
connection. In the interest of avoiding corruption from
opening a different font than the old one (we already have
its metrics, extents, and probably some of its glyphs),
verify that the metrics and properties all match. */
if (fs_fonts_match (pInfo, &bfont->pfont->info))
{
err = Successful;
bfont->state = FS_DONE_REPLY;
}
else
{
fs_cleanup_bfont (bfont);
err = BadFontName;
}
_fs_free_props (pInfo);
return err;
}
/*
* Ask for terminal format fonts if possible
*/
if (bfont->pfont->info.terminalFont)
bfont->format = ((bfont->format & ~ (BitmapFormatImageRectMask)) |
BitmapFormatImageRectMax);
/*
* Figure out if the whole font should get loaded right now.
*/
if (glyphCachingMode == CACHING_OFF ||
(glyphCachingMode == CACHE_16_BIT_GLYPHS
&& !bfont->pfont->info.lastRow))
{
bfont->flags |= FontLoadAll;
}
/*
* Ready to send the query bitmaps; the terminal font bit has
* been computed and glyphCaching has been considered
*/
if (bfont->flags & FontLoadBitmaps)
{
fs_send_query_bitmaps (fpe, blockrec);
_fs_flush (conn);
}
bfont->state = FS_EXTENT_REPLY;
/*
* Reset the blockrec for the next reply
*/
blockrec->sequenceNumber = bfont->queryExtentsSequence;
conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
return StillWorking;
}
static int
fs_read_extent_info(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
FSBlockedFontPtr bfont = (FSBlockedFontPtr) blockrec->data;
FSFontDataPtr fsd = (FSFontDataPtr) bfont->pfont->fpePrivate;
FSFontPtr fsfont = (FSFontPtr) bfont->pfont->fontPrivate;
fsQueryXExtents16Reply *rep;
char *buf;
int i;
int numExtents;
int numInfos;
int ret;
Bool haveInk = FALSE; /* need separate ink metrics? */
CharInfoPtr ci, pCI;
char *fsci;
fsXCharInfo fscilocal;
FontInfoRec *fi = &bfont->pfont->info;
rep = (fsQueryXExtents16Reply *) fs_get_reply (conn, &ret);
if (!rep || rep->type == FS_Error)
{
if (ret == FSIO_BLOCK)
return StillWorking;
if (rep)
_fs_done_read (conn, rep->length << 2);
fs_cleanup_bfont (bfont);
return BadFontName;
}
/* move the data over */
/* need separate inkMetrics for fixed font server protocol version */
numExtents = rep->num_extents;
numInfos = numExtents;
if (bfont->pfont->info.terminalFont && conn->fsMajorVersion > 1)
{
numInfos *= 2;
haveInk = TRUE;
}
ci = pCI = (CharInfoPtr) xalloc(sizeof(CharInfoRec) * numInfos);
if (!pCI)
{
_fs_done_read (conn, rep->length << 2);
fs_cleanup_bfont(bfont);
return AllocError;
}
fsfont->encoding = pCI;
if (haveInk)
fsfont->inkMetrics = pCI + numExtents;
else
fsfont->inkMetrics = pCI;
buf = (char *) rep;
buf += SIZEOF (fsQueryXExtents16Reply);
fsci = buf;
fsd->glyphs_to_get = 0;
ci = fsfont->inkMetrics;
for (i = 0; i < numExtents; i++)
{
memcpy(&fscilocal, fsci, SIZEOF(fsXCharInfo)); /* align it */
_fs_convert_char_info(&fscilocal, &ci->metrics);
/* Bounds check. */
if (ci->metrics.ascent > fi->maxbounds.ascent)
{
ErrorF("fserve: warning: %s %s ascent (%d) > maxascent (%d)\n",
fpe->name, fsd->name,
ci->metrics.ascent, fi->maxbounds.ascent);
ci->metrics.ascent = fi->maxbounds.ascent;
}
if (ci->metrics.descent > fi->maxbounds.descent)
{
ErrorF("fserve: warning: %s %s descent (%d) > maxdescent (%d)\n",
fpe->name, fsd->name,
ci->metrics.descent, fi->maxbounds.descent);
ci->metrics.descent = fi->maxbounds.descent;
}
fsci = fsci + SIZEOF(fsXCharInfo);
/* Initialize the bits field for later glyph-caching use */
if (NONZEROMETRICS(&ci->metrics))
{
if (!haveInk &&
(ci->metrics.leftSideBearing == ci->metrics.rightSideBearing ||
ci->metrics.ascent == -ci->metrics.descent))
pCI[i].bits = &_fs_glyph_zero_length;
else
{
pCI[i].bits = &_fs_glyph_undefined;
fsd->glyphs_to_get++;
}
}
else
pCI[i].bits = (char *)0;
ci++;
}
/* Done with reply */
_fs_done_read (conn, rep->length << 2);
/* build bitmap metrics, ImageRectMax style */
if (haveInk)
{
CharInfoPtr ii;
ci = fsfont->encoding;
ii = fsfont->inkMetrics;
for (i = 0; i < numExtents; i++, ci++, ii++)
{
if (NONZEROMETRICS(&ii->metrics))
{
ci->metrics.leftSideBearing = FONT_MIN_LEFT(fi);
ci->metrics.rightSideBearing = FONT_MAX_RIGHT(fi);
ci->metrics.ascent = FONT_MAX_ASCENT(fi);
ci->metrics.descent = FONT_MAX_DESCENT(fi);
ci->metrics.characterWidth = FONT_MAX_WIDTH(fi);
ci->metrics.attributes = ii->metrics.attributes;
}
else
{
ci->metrics = ii->metrics;
}
/* Bounds check. */
if (ci->metrics.ascent > fi->maxbounds.ascent)
{
ErrorF("fserve: warning: %s %s ascent (%d) "
"> maxascent (%d)\n",
fpe->name, fsd->name,
ci->metrics.ascent, fi->maxbounds.ascent);
ci->metrics.ascent = fi->maxbounds.ascent;
}
if (ci->metrics.descent > fi->maxbounds.descent)
{
ErrorF("fserve: warning: %s %s descent (%d) "
"> maxdescent (%d)\n",
fpe->name, fsd->name,
ci->metrics.descent, fi->maxbounds.descent);
ci->metrics.descent = fi->maxbounds.descent;
}
}
}
{
unsigned int r, c, numCols, firstCol;
firstCol = bfont->pfont->info.firstCol;
numCols = bfont->pfont->info.lastCol - firstCol + 1;
c = bfont->pfont->info.defaultCh;
fsfont->pDefault = 0;
if (bfont->pfont->info.lastRow)
{
r = c >> 8;
r -= bfont->pfont->info.firstRow;
c &= 0xff;
c -= firstCol;
if (r < bfont->pfont->info.lastRow-bfont->pfont->info.firstRow+1 &&
c < numCols)
fsfont->pDefault = &pCI[r * numCols + c];
}
else
{
c -= firstCol;
if (c < numCols)
fsfont->pDefault = &pCI[c];
}
}
bfont->state = FS_GLYPHS_REPLY;
if (bfont->flags & FontLoadBitmaps)
{
/*
* Reset the blockrec for the next reply
*/
blockrec->sequenceNumber = bfont->queryBitmapsSequence;
conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
return StillWorking;
}
return Successful;
}
#ifdef DEBUG
static char *fs_open_states[] = {
"OPEN_REPLY ",
"INFO_REPLY ",
"EXTENT_REPLY",
"GLYPHS_REPLY",
"DONE_REPLY ",
"DEPENDING ",
};
#endif
static int
fs_do_open_font(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
{
FSBlockedFontPtr bfont = (FSBlockedFontPtr) blockrec->data;
int err;
#ifdef DEBUG
fprintf (stderr, "fs_do_open_font state %s %s\n",
fs_open_states[bfont->state],
((FSFontDataPtr) (bfont->pfont->fpePrivate))->name);
#endif
err = BadFontName;
switch (bfont->state) {
case FS_OPEN_REPLY:
err = fs_read_open_font(fpe, blockrec);
if (err != StillWorking) { /* already loaded, or error */
/* if font's already loaded, massage error code */
switch (bfont->state) {
case FS_DONE_REPLY:
err = Successful;
break;
case FS_DEPENDING:
err = StillWorking;
break;
}
}
break;
case FS_INFO_REPLY:
err = fs_read_query_info(fpe, blockrec);
break;
case FS_EXTENT_REPLY:
err = fs_read_extent_info(fpe, blockrec);
break;
case FS_GLYPHS_REPLY:
if (bfont->flags & FontLoadBitmaps)
err = fs_read_glyphs(fpe, blockrec);
break;
case FS_DEPENDING: /* can't happen */
default:
break;
}
#ifdef DEBUG
fprintf (stderr, "fs_do_open_font err %d\n", err);
#endif
if (err != StillWorking)
{
bfont->state = FS_DONE_REPLY; /* for _fs_load_glyphs() */
while ((blockrec = blockrec->depending))
{
bfont = (FSBlockedFontPtr) blockrec->data;
bfont->state = FS_DONE_REPLY; /* for _fs_load_glyphs() */
}
}
return err;
}
void
_fs_mark_block (FSFpePtr conn, CARD32 mask)
{
conn->blockState |= mask;
fs_blockState |= mask;
}
void
_fs_unmark_block (FSFpePtr conn, CARD32 mask)
{
FSFpePtr c;
if (conn->blockState & mask)
{
conn->blockState &= ~mask;
fs_blockState = 0;
for (c = fs_fpes; c; c = c->next)
fs_blockState |= c->blockState;
}
}
/* ARGSUSED */
static void
fs_block_handler(pointer data, OSTimePtr wt, pointer LastSelectMask)
{
static struct timeval block_timeout;
CARD32 now, earliest, wakeup;
int soonest;
FSFpePtr conn;
XFD_ORSET((fd_set *)LastSelectMask, (fd_set *)LastSelectMask,
&_fs_fd_mask);
/*
* Flush all pending output
*/
if (fs_blockState & FS_PENDING_WRITE)
for (conn = fs_fpes; conn; conn = conn->next)
if (conn->blockState & FS_PENDING_WRITE)
_fs_flush (conn);
/*
* Check for any fpe with a complete reply, set sleep time to zero
*/
if (fs_blockState & FS_COMPLETE_REPLY)
{
block_timeout.tv_sec = 0;
block_timeout.tv_usec = 0;
if (*wt == NULL)
*wt = &block_timeout;
else
**wt = block_timeout;
}
/*
* Walk through fpe list computing sleep time
*/
else if (fs_blockState & (FS_BROKEN_WRITE|
FS_BROKEN_CONNECTION|
FS_PENDING_REPLY|
FS_RECONNECTING))
{
now = GetTimeInMillis ();
earliest = now + 10000000;
for (conn = fs_fpes; conn; conn = conn->next)
{
if (conn->blockState & FS_RECONNECTING)
{
wakeup = conn->blockedConnectTime;
if (TimeCmp (wakeup, <, earliest))
earliest = wakeup;
}
if (conn->blockState & FS_BROKEN_CONNECTION)
{
wakeup = conn->brokenConnectionTime;
if (TimeCmp (wakeup, <, earliest))
earliest = wakeup;
}
if (conn->blockState & FS_BROKEN_WRITE)
{
wakeup = conn->brokenWriteTime;
if (TimeCmp (wakeup, <, earliest))
earliest = wakeup;
}
if (conn->blockState & FS_PENDING_REPLY)
{
wakeup = conn->blockedReplyTime;
if (TimeCmp (wakeup, <, earliest))
earliest = wakeup;
}
}
soonest = earliest - now;
if (soonest < 0)
soonest = 0;
block_timeout.tv_sec = soonest / 1000;
block_timeout.tv_usec = (soonest % 1000) * 1000;
if (*wt == NULL)
*wt = &block_timeout;
else if (soonest < (*wt)->tv_sec * 1000 + (*wt)->tv_usec / 1000)
**wt = block_timeout;
}
}
static void
fs_handle_unexpected(FSFpePtr conn, fsGenericReply *rep)
{
if (rep->type == FS_Event && rep->data1 == KeepAlive)
{
fsNoopReq req;
/* ping it back */
req.reqType = FS_Noop;
req.length = SIZEOF(fsNoopReq) >> 2;
_fs_add_req_log(conn, FS_Noop);
_fs_write(conn, (char *) &req, SIZEOF(fsNoopReq));
}
/* this should suck up unexpected replies and events */
_fs_done_read (conn, rep->length << 2);
}
static void
fs_read_reply (FontPathElementPtr fpe, pointer client)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
FSBlockDataPtr blockrec;
int ret;
int err;
fsGenericReply *rep;
if ((rep = fs_get_reply (conn, &ret)))
{
_fs_add_rep_log (conn, rep);
for (blockrec = conn->blockedRequests;
blockrec;
blockrec = blockrec->next)
{
if (blockrec->sequenceNumber == rep->sequenceNumber)
break;
}
err = Successful;
if (!blockrec)
{
fs_handle_unexpected(conn, rep);
}
else
{
/*
* go read it, and if we're done,
* wake up the appropriate client
*/
switch (blockrec->type) {
case FS_OPEN_FONT:
blockrec->errcode = fs_do_open_font(fpe, blockrec);
break;
case FS_LOAD_GLYPHS:
blockrec->errcode = fs_read_glyphs(fpe, blockrec);
break;
case FS_LIST_FONTS:
blockrec->errcode = fs_read_list(fpe, blockrec);
break;
case FS_LIST_WITH_INFO:
blockrec->errcode = fs_read_list_info(fpe, blockrec);
break;
default:
break;
}
err = blockrec->errcode;
if (err != StillWorking)
{
while (blockrec)
{
blockrec->errcode = err;
if (client != blockrec->client)
ClientSignal(blockrec->client);
blockrec = blockrec->depending;
}
_fs_unmark_block (conn, FS_PENDING_REPLY);
}
}
if (fs_reply_ready (conn))
_fs_mark_block (conn, FS_COMPLETE_REPLY);
else
_fs_unmark_block (conn, FS_COMPLETE_REPLY);
}
}
static int
fs_wakeup(FontPathElementPtr fpe, unsigned long *mask)
{
fd_set *LastSelectMask = (fd_set *) mask;
FSFpePtr conn = (FSFpePtr) fpe->private;
/*
* Don't continue if the fd is -1 (which will be true when the
* font server terminates
*/
if ((conn->blockState & FS_RECONNECTING))
_fs_check_reconnect (conn);
else if ((conn->blockState & FS_COMPLETE_REPLY) ||
(conn->fs_fd != -1 && FD_ISSET(conn->fs_fd, LastSelectMask)))
fs_read_reply (fpe, 0);
if (conn->blockState & (FS_PENDING_REPLY|FS_BROKEN_CONNECTION|FS_BROKEN_WRITE))
_fs_do_blocked (conn);
#ifdef DEBUG
{
FSBlockDataPtr blockrec;
FSBlockedFontPtr bfont;
FSBlockedListPtr blist;
static CARD32 lastState;
static FSBlockDataPtr lastBlock;
if (conn->blockState || conn->blockedRequests || lastState || lastBlock)
{
fprintf (stderr, " Block State 0x%x\n", (int) conn->blockState);
lastState = conn->blockState;
lastBlock = conn->blockedRequests;
}
for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
{
switch (blockrec->type) {
case FS_OPEN_FONT:
bfont = (FSBlockedFontPtr) blockrec->data;
fprintf (stderr, " Blocked font errcode %d sequence %d state %s %s\n",
blockrec->errcode,
blockrec->sequenceNumber,
fs_open_states[bfont->state],
bfont->pfont ?
((FSFontDataPtr) (bfont->pfont->fpePrivate))->name :
"<freed>");
break;
case FS_LIST_FONTS:
blist = (FSBlockedListPtr) blockrec->data;
fprintf (stderr, " Blocked list errcode %d sequence %d\n",
blockrec->errcode, blockrec->sequenceNumber);
break;
default:
fprintf (stderr, " Blocked type %d errcode %d sequence %d\n",
blockrec->type,
blockrec->errcode,
blockrec->sequenceNumber);
break;
}
}
}
#endif
return FALSE;
}
/*
* Notice a dead connection and prepare for reconnect
*/
void
_fs_connection_died(FSFpePtr conn)
{
if (conn->blockState & FS_BROKEN_CONNECTION)
return;
fs_close_conn(conn);
conn->brokenConnectionTime = GetTimeInMillis ();
_fs_mark_block (conn, FS_BROKEN_CONNECTION);
_fs_unmark_block (conn, FS_BROKEN_WRITE|FS_PENDING_WRITE|FS_RECONNECTING);
}
/*
* Signal clients that the connection has come back up
*/
static int
_fs_restart_connection(FSFpePtr conn)
{
FSBlockDataPtr block;
_fs_unmark_block (conn, FS_GIVE_UP);
while ((block = (FSBlockDataPtr) conn->blockedRequests))
{
if (block->errcode == StillWorking)
{
ClientSignal(block->client);
fs_abort_blockrec(conn, block);
}
}
return TRUE;
}
/*
* Declare this font server connection useless
*/
static void
_fs_giveup (FSFpePtr conn)
{
FSBlockDataPtr block;
if (conn->blockState & FS_GIVE_UP)
return;
#ifdef DEBUG
fprintf (stderr, "give up on FS \"%s\"\n", conn->servername);
#endif
_fs_mark_block (conn, FS_GIVE_UP);
while ((block = (FSBlockDataPtr) conn->blockedRequests))
{
if (block->errcode == StillWorking)
{
ClientSignal (block->client);
fs_abort_blockrec (conn, block);
}
}
if (conn->fs_fd >= 0)
_fs_connection_died (conn);
}
static void
_fs_do_blocked (FSFpePtr conn)
{
CARD32 now;
now = GetTimeInMillis ();
if ((conn->blockState & FS_PENDING_REPLY) &&
TimeCmp (conn->blockedReplyTime, <=, now))
{
_fs_giveup (conn);
}
else
{
if (conn->blockState & FS_BROKEN_CONNECTION)
{
/* Try to reconnect broken connections */
if (TimeCmp (conn->brokenConnectionTime, <=, now))
_fs_start_reconnect (conn);
}
else if (conn->blockState & FS_BROKEN_WRITE)
{
/* Try to flush blocked connections */
if (TimeCmp (conn->brokenWriteTime, <=, now))
_fs_flush (conn);
}
}
}
/*
* sends the actual request out
*/
/* ARGSUSED */
static int
fs_send_open_font(pointer client, FontPathElementPtr fpe, Mask flags,
char *name, int namelen,
fsBitmapFormat format, fsBitmapFormatMask fmask,
XID id, FontPtr *ppfont)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
FontPtr font;
FSBlockDataPtr blockrec = NULL;
FSBlockedFontPtr bfont;
FSFontDataPtr fsd;
fsOpenBitmapFontReq openreq;
fsQueryXInfoReq inforeq;
fsQueryXExtents16Req extreq;
int err;
unsigned char buf[1024];
if (conn->blockState & FS_GIVE_UP)
return BadFontName;
if (namelen <= 0 || namelen > sizeof (buf) - 1)
return BadFontName;
/*
* Get the font structure put together, either by reusing
* the existing one or creating a new one
*/
if (flags & FontReopen)
{
Atom nameatom, fn = None;
int i;
font = *ppfont;
fsd = (FSFontDataPtr)font->fpePrivate;
/* This is an attempt to reopen a font. Did the font have a
NAME property? */
if ((nameatom = MakeAtom("FONT", 4, 0)) != None)
{
for (i = 0; i < font->info.nprops; i++)
if (font->info.props[i].name == nameatom &&
font->info.isStringProp[i])
{
fn = font->info.props[i].value;
break;
}
}
if (fn == None || !(name = NameForAtom(fn)))
{
name = fsd->name;
namelen = fsd->namelen;
}
else
namelen = strlen(name);
}
else
{
font = fs_create_font (fpe, name, namelen, format, fmask);
if (!font)
return AllocError;
fsd = (FSFontDataPtr)font->fpePrivate;
}
/* make a new block record, and add it to the end of the list */
blockrec = fs_new_block_rec(font->fpe, client, FS_OPEN_FONT);
if (!blockrec)
{
if (!(flags & FontReopen))
(*font->unload_font) (font);
return AllocError;
}
/*
* Must check this before generating any protocol, otherwise we'll
* mess up a reconnect in progress
*/
if (conn->blockState & (FS_BROKEN_CONNECTION | FS_RECONNECTING))
{
_fs_pending_reply (conn);
return Suspended;
}
fsd->generation = conn->generation;
bfont = (FSBlockedFontPtr) blockrec->data;
bfont->fontid = fsd->fontid;
bfont->pfont = font;
bfont->state = FS_OPEN_REPLY;
bfont->flags = flags;
bfont->format = fsd->format;
bfont->clients_depending = (FSClientsDependingPtr)0;
bfont->freeFont = (flags & FontReopen) == 0;
_fs_client_access (conn, client, (flags & FontOpenSync) != 0);
_fs_client_resolution(conn);
/* do an FS_OpenFont, FS_QueryXInfo and FS_QueryXExtents */
buf[0] = (unsigned char) namelen;
memcpy(&buf[1], name, namelen);
openreq.reqType = FS_OpenBitmapFont;
openreq.fid = fsd->fontid;
openreq.format_hint = fsd->format;
openreq.format_mask = fsd->fmask;
openreq.length = (SIZEOF(fsOpenBitmapFontReq) + namelen + 4) >> 2;
_fs_add_req_log(conn, FS_OpenBitmapFont);
_fs_write(conn, (char *) &openreq, SIZEOF(fsOpenBitmapFontReq));
_fs_write_pad(conn, (char *) buf, namelen + 1);
blockrec->sequenceNumber = conn->current_seq;
inforeq.reqType = FS_QueryXInfo;
inforeq.id = fsd->fontid;
inforeq.length = SIZEOF(fsQueryXInfoReq) >> 2;
bfont->queryInfoSequence = conn->current_seq + 1;
_fs_add_req_log(conn, FS_QueryXInfo);
_fs_write(conn, (char *) &inforeq, SIZEOF(fsQueryXInfoReq));
if (!(bfont->flags & FontReopen))
{
extreq.reqType = FS_QueryXExtents16;
extreq.range = fsTrue;
extreq.fid = fsd->fontid;
extreq.num_ranges = 0;
extreq.length = SIZEOF(fsQueryXExtents16Req) >> 2;
bfont->queryExtentsSequence = conn->current_seq + 1;
_fs_add_req_log(conn, FS_QueryXExtents16);
_fs_write(conn, (char *) &extreq, SIZEOF(fsQueryXExtents16Req));
}
#ifdef NCD
if (configData.ExtendedFontDiags)
{
memcpy(buf, name, MIN(256, namelen));
buf[MIN(256, namelen)] = '\0';
printf("Requesting font \"%s\" from font server \"%s\"\n",
buf, font->fpe->name);
}
#endif
_fs_prepare_for_reply (conn);
err = blockrec->errcode;
if (bfont->flags & FontOpenSync)
{
while (blockrec->errcode == StillWorking)
{
if (fs_await_reply (conn) != FSIO_READY)
{
blockrec->errcode = BadFontName;
break;
}
fs_read_reply (font->fpe, client);
}
err = blockrec->errcode;
if (err == Successful)
*ppfont = bfont->pfont;
else
fs_cleanup_bfont (bfont);
bfont->freeFont = FALSE;
_fs_remove_block_rec (conn, blockrec);
}
return err == StillWorking ? Suspended : err;
}
static void
fs_send_query_bitmaps(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
FSBlockedFontPtr bfont = (FSBlockedFontPtr) blockrec->data;
fsQueryXBitmaps16Req bitreq;
/* send the request */
bitreq.reqType = FS_QueryXBitmaps16;
bitreq.fid = bfont->fontid;
bitreq.format = bfont->format;
bitreq.range = TRUE;
bitreq.length = SIZEOF(fsQueryXBitmaps16Req) >> 2;
bitreq.num_ranges = 0;
bfont->queryBitmapsSequence = conn->current_seq + 1;
_fs_add_req_log(conn, FS_QueryXBitmaps16);
_fs_write(conn, (char *) &bitreq, SIZEOF(fsQueryXBitmaps16Req));
}
/* ARGSUSED */
static int
fs_open_font(pointer client, FontPathElementPtr fpe, Mask flags,
char *name, int namelen,
fsBitmapFormat format, fsBitmapFormatMask fmask,
XID id, FontPtr *ppfont,
char **alias, FontPtr non_cachable_font)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
FSBlockDataPtr blockrec;
FSBlockedFontPtr bfont;
int err;
/* libfont interface expects ImageRectMin glyphs */
format = (format & ~BitmapFormatImageRectMask) | BitmapFormatImageRectMin;
*alias = (char *) 0;
for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
{
if (blockrec->type == FS_OPEN_FONT && blockrec->client == client)
{
err = blockrec->errcode;
if (err == StillWorking)
return Suspended;
bfont = (FSBlockedFontPtr) blockrec->data;
if (err == Successful)
*ppfont = bfont->pfont;
else
fs_cleanup_bfont (bfont);
_fs_remove_block_rec (conn, blockrec);
return err;
}
}
return fs_send_open_font(client, fpe, flags, name, namelen, format, fmask,
id, ppfont);
}
/* ARGSUSED */
static int
fs_send_close_font(FontPathElementPtr fpe, Font id)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
fsCloseReq req;
if (conn->blockState & FS_GIVE_UP)
return Successful;
/* tell the font server to close the font */
req.reqType = FS_CloseFont;
req.length = SIZEOF(fsCloseReq) >> 2;
req.id = id;
_fs_add_req_log(conn, FS_CloseFont);
_fs_write(conn, (char *) &req, SIZEOF(fsCloseReq));
return Successful;
}
/* ARGSUSED */
static void
fs_close_font(FontPathElementPtr fpe, FontPtr pfont)
{
FSFontDataPtr fsd = (FSFontDataPtr) pfont->fpePrivate;
FSFpePtr conn = (FSFpePtr) fpe->private;
if (conn->generation == fsd->generation)
fs_send_close_font(fpe, fsd->fontid);
#ifdef DEBUG
{
FSBlockDataPtr blockrec;
FSBlockedFontPtr bfont;
for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
{
if (blockrec->type == FS_OPEN_FONT)
{
bfont = (FSBlockedFontPtr) blockrec->data;
if (bfont->pfont == pfont)
fprintf (stderr, "closing font which hasn't been opened\n");
}
}
}
#endif
(*pfont->unload_font) (pfont);
}
static int
fs_read_glyphs(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
{
FSBlockedGlyphPtr bglyph = (FSBlockedGlyphPtr) blockrec->data;
FSBlockedFontPtr bfont = (FSBlockedFontPtr) blockrec->data;
FSFpePtr conn = (FSFpePtr) fpe->private;
FontPtr pfont = bglyph->pfont;
/* works for either blocked font
or glyph rec... pfont is at
the very beginning of both
blockrec->data structures */
FSFontDataPtr fsd = (FSFontDataPtr) (pfont->fpePrivate);
FSFontPtr fsdata = (FSFontPtr) pfont->fontPrivate;
FontInfoPtr pfi = &pfont->info;
fsQueryXBitmaps16Reply *rep;
char *buf;
fsOffset32 *ppbits;
fsOffset32 local_off;
char *off_adr;
pointer pbitmaps;
char *bits, *allbits;
#ifdef DEBUG
char *origallbits;
#endif
int i,
err;
int nranges = 0;
int ret;
fsRange *nextrange = 0;
unsigned long minchar, maxchar;
rep = (fsQueryXBitmaps16Reply *) fs_get_reply (conn, &ret);
if (!rep || rep->type == FS_Error)
{
if (ret == FSIO_BLOCK)
return StillWorking;
if (rep)
_fs_done_read (conn, rep->length << 2);
err = AllocError;
goto bail;
}
buf = (char *) rep;
buf += SIZEOF (fsQueryXBitmaps16Reply);
ppbits = (fsOffset32 *) buf;
buf += SIZEOF (fsOffset32) * (rep->num_chars);
pbitmaps = (pointer ) buf;
if (blockrec->type == FS_LOAD_GLYPHS)
{
nranges = bglyph->num_expected_ranges;
nextrange = bglyph->expected_ranges;
}
/* place the incoming glyphs */
if (nranges)
{
/* We're operating under the assumption that the ranges
requested in the LoadGlyphs call were all legal for this
font, and that individual ranges do not cover multiple
rows... fs_build_range() is designed to ensure this. */
minchar = (nextrange->min_char_high - pfi->firstRow) *
(pfi->lastCol - pfi->firstCol + 1) +
nextrange->min_char_low - pfi->firstCol;
maxchar = (nextrange->max_char_high - pfi->firstRow) *
(pfi->lastCol - pfi->firstCol + 1) +
nextrange->max_char_low - pfi->firstCol;
nextrange++;
}
else
{
minchar = 0;
maxchar = rep->num_chars;
}
off_adr = (char *)ppbits;
allbits = fs_alloc_glyphs (pfont, rep->nbytes);
if (!allbits)
{
err = AllocError;
goto bail;
}
#ifdef DEBUG
origallbits = allbits;
fprintf (stderr, "Reading %d glyphs in %d bytes for %s\n",
(int) rep->num_chars, (int) rep->nbytes, fsd->name);
#endif
for (i = 0; i < rep->num_chars; i++)
{
memcpy(&local_off, off_adr, SIZEOF(fsOffset32)); /* align it */
if (blockrec->type == FS_OPEN_FONT ||
fsdata->encoding[minchar].bits == &_fs_glyph_requested)
{
/*
* Broken X font server returns bits for missing characters
* when font is padded
*/
if (NONZEROMETRICS(&fsdata->encoding[minchar].metrics))
{
if (local_off.length)
{
bits = allbits;
allbits += local_off.length;
memcpy(bits, (char *)pbitmaps + local_off.position,
local_off.length);
}
else
bits = &_fs_glyph_zero_length;
}
else
bits = 0;
if (fsdata->encoding[minchar].bits == &_fs_glyph_requested)
fsd->glyphs_to_get--;
fsdata->encoding[minchar].bits = bits;
}
if (minchar++ == maxchar)
{
if (!--nranges) break;
minchar = (nextrange->min_char_high - pfi->firstRow) *
(pfi->lastCol - pfi->firstCol + 1) +
nextrange->min_char_low - pfi->firstCol;
maxchar = (nextrange->max_char_high - pfi->firstRow) *
(pfi->lastCol - pfi->firstCol + 1) +
nextrange->max_char_low - pfi->firstCol;
nextrange++;
}
off_adr += SIZEOF(fsOffset32);
}
#ifdef DEBUG
fprintf (stderr, "Used %d bytes instead of %d\n",
(int) (allbits - origallbits), (int) rep->nbytes);
#endif
if (blockrec->type == FS_OPEN_FONT)
{
fsd->glyphs_to_get = 0;
bfont->state = FS_DONE_REPLY;
}
err = Successful;
bail:
_fs_done_read (conn, rep->length << 2);
return err;
}
static int
fs_send_load_glyphs(pointer client, FontPtr pfont,
int nranges, fsRange *ranges)
{
FontPathElementPtr fpe = pfont->fpe;
FSFpePtr conn = (FSFpePtr) fpe->private;
FSBlockedGlyphPtr blockedglyph;
fsQueryXBitmaps16Req req;
FSBlockDataPtr blockrec;
if (conn->blockState & FS_GIVE_UP)
return BadCharRange;
/* make a new block record, and add it to the end of the list */
blockrec = fs_new_block_rec(fpe, client, FS_LOAD_GLYPHS);
if (!blockrec)
return AllocError;
blockedglyph = (FSBlockedGlyphPtr) blockrec->data;
blockedglyph->pfont = pfont;
blockedglyph->num_expected_ranges = nranges;
/* Assumption: it's our job to free ranges */
blockedglyph->expected_ranges = ranges;
blockedglyph->clients_depending = (FSClientsDependingPtr)0;
if (conn->blockState & (FS_BROKEN_CONNECTION|FS_RECONNECTING))
{
_fs_pending_reply (conn);
return Suspended;
}
/* send the request */
req.reqType = FS_QueryXBitmaps16;
req.fid = ((FSFontDataPtr) pfont->fpePrivate)->fontid;
req.format = pfont->format;
if (pfont->info.terminalFont)
req.format = (req.format & ~(BitmapFormatImageRectMask)) |
BitmapFormatImageRectMax;
req.range = TRUE;
/* each range takes up 4 bytes */
req.length = (SIZEOF(fsQueryXBitmaps16Req) >> 2) + nranges;
req.num_ranges = nranges * 2; /* protocol wants count of fsChar2bs */
_fs_add_req_log(conn, FS_QueryXBitmaps16);
_fs_write(conn, (char *) &req, SIZEOF(fsQueryXBitmaps16Req));
blockrec->sequenceNumber = conn->current_seq;
/* Send ranges to the server... pack into a char array by hand
to avoid structure-packing portability problems and to
handle swapping for version1 protocol */
if (nranges)
{
#define RANGE_BUFFER_SIZE 64
#define RANGE_BUFFER_SIZE_MASK 63
int i;
char range_buffer[RANGE_BUFFER_SIZE * 4];
char *range_buffer_p;
range_buffer_p = range_buffer;
for (i = 0; i < nranges;)
{
if (conn->fsMajorVersion > 1)
{
*range_buffer_p++ = ranges[i].min_char_high;
*range_buffer_p++ = ranges[i].min_char_low;
*range_buffer_p++ = ranges[i].max_char_high;
*range_buffer_p++ = ranges[i].max_char_low;
}
else
{
*range_buffer_p++ = ranges[i].min_char_low;
*range_buffer_p++ = ranges[i].min_char_high;
*range_buffer_p++ = ranges[i].max_char_low;
*range_buffer_p++ = ranges[i].max_char_high;
}
if (!(++i & RANGE_BUFFER_SIZE_MASK))
{
_fs_write(conn, range_buffer, RANGE_BUFFER_SIZE * 4);
range_buffer_p = range_buffer;
}
}
if (i &= RANGE_BUFFER_SIZE_MASK)
_fs_write(conn, range_buffer, i * 4);
}
_fs_prepare_for_reply (conn);
return Suspended;
}
extern pointer serverClient; /* This could be any number that
doesn't conflict with existing
client values. */
static int
_fs_load_glyphs(pointer client, FontPtr pfont, Bool range_flag,
unsigned int nchars, int item_size, unsigned char *data)
{
FSFpePtr conn = (FSFpePtr) pfont->fpe->private;
int nranges = 0;
fsRange *ranges = NULL;
int res;
FSBlockDataPtr blockrec;
FSBlockedGlyphPtr blockedglyph;
FSClientsDependingPtr *clients_depending = NULL;
int err;
/* see if the result is already there */
for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
{
if (blockrec->type == FS_LOAD_GLYPHS)
{
blockedglyph = (FSBlockedGlyphPtr) blockrec->data;
if (blockedglyph->pfont == pfont)
{
/* Look for this request */
if (blockrec->client == client)
{
err = blockrec->errcode;
if (err == StillWorking)
return Suspended;
_fs_signal_clients_depending(&blockedglyph->clients_depending);
_fs_remove_block_rec(conn, blockrec);
return err;
}
/* We've found an existing LoadGlyphs blockrec for this
font but for another client. Rather than build a
blockrec for it now (which entails some complex
maintenance), we'll add it to a queue of clients to
be signalled when the existing LoadGlyphs is
completed. */
clients_depending = &blockedglyph->clients_depending;
break;
}
}
else if (blockrec->type == FS_OPEN_FONT)
{
FSBlockedFontPtr bfont;
bfont = (FSBlockedFontPtr) blockrec->data;
if (bfont->pfont == pfont)
{
/*
* An OpenFont is pending for this font, this must
* be from a reopen attempt, so finish the open
* attempt and retry the LoadGlyphs
*/
if (blockrec->client == client)
{
err = blockrec->errcode;
if (err == StillWorking)
return Suspended;
_fs_signal_clients_depending(&bfont->clients_depending);
_fs_remove_block_rec(conn, blockrec);
if (err != Successful)
return err;
break;
}
/* We've found an existing OpenFont blockrec for this
font but for another client. Rather than build a
blockrec for it now (which entails some complex
maintenance), we'll add it to a queue of clients to
be signalled when the existing OpenFont is
completed. */
if (blockrec->errcode == StillWorking)
{
clients_depending = &bfont->clients_depending;
break;
}
}
}
}
/*
* see if the desired glyphs already exist, and return Successful if they
* do, otherwise build up character range/character string
*/
res = fs_build_range(pfont, range_flag, nchars, item_size, data,
&nranges, &ranges);
switch (res)
{
case AccessDone:
return Successful;
case Successful:
break;
default:
return res;
}
/*
* If clients_depending is not null, this request must wait for
* some prior request(s) to complete.
*/
if (clients_depending)
{
/* Since we're not ready to send the load_glyphs request yet,
clean up the damage (if any) caused by the fs_build_range()
call. */
if (nranges)
{
_fs_clean_aborted_loadglyphs(pfont, nranges, ranges);
xfree(ranges);
}
return _fs_add_clients_depending(clients_depending, client);
}
/*
* If fsd->generation != conn->generation, the font has been closed
* due to a lost connection. We will reopen it, which will result
* in one of three things happening:
* 1) The open will succeed and obtain the same font. Life
* is wonderful.
* 2) The open will fail. There is code above to recognize this
* and flunk the LoadGlyphs request. The client might not be
* thrilled.
* 3) Worst case: the open will succeed but the font we open will
* be different. The fs_read_query_info() procedure attempts
* to detect this by comparing the existing metrics and
* properties against those of the reopened font... if they
* don't match, we flunk the reopen, which eventually results
* in flunking the LoadGlyphs request. We could go a step
* further and compare the extents, but this should be
* sufficient.
*/
if (((FSFontDataPtr)pfont->fpePrivate)->generation != conn->generation)
{
/* Since we're not ready to send the load_glyphs request yet,
clean up the damage caused by the fs_build_range() call. */
_fs_clean_aborted_loadglyphs(pfont, nranges, ranges);
xfree(ranges);
/* Now try to reopen the font. */
return fs_send_open_font(client, pfont->fpe,
(Mask)FontReopen, (char *)0, 0,
(fsBitmapFormat)0, (fsBitmapFormatMask)0,
(XID)0, &pfont);
}
return fs_send_load_glyphs(client, pfont, nranges, ranges);
}
int
fs_load_all_glyphs(FontPtr pfont)
{
int err;
FSFpePtr conn = (FSFpePtr) pfont->fpe->private;
/*
* The purpose of this procedure is to load all glyphs in the event
* that we're dealing with someone who doesn't understand the finer
* points of glyph caching... it is called from _fs_get_glyphs() if
* the latter is called to get glyphs that have not yet been loaded.
* We assume that the caller will not know how to handle a return
* value of Suspended (usually the case for a GetGlyphs() caller),
* so this procedure hangs around, freezing the server, for the
* request to complete. This is an unpleasant kluge called to
* perform an unpleasant job that, we hope, will never be required.
*/
while ((err = _fs_load_glyphs(serverClient, pfont, TRUE, 0, 0, NULL)) ==
Suspended)
{
if (fs_await_reply (conn) != FSIO_READY)
{
/* Get rid of blockrec */
fs_client_died(serverClient, pfont->fpe);
err = BadCharRange;
break;
}
fs_read_reply (pfont->fpe, serverClient);
}
return err;
}
static int
fs_read_list(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
FSBlockedListPtr blist = (FSBlockedListPtr) blockrec->data;
fsListFontsReply *rep;
char *data;
int length,
i,
ret;
int err;
rep = (fsListFontsReply *) fs_get_reply (conn, &ret);
if (!rep || rep->type == FS_Error)
{
if (ret == FSIO_BLOCK)
return StillWorking;
if (rep)
_fs_done_read (conn, rep->length << 2);
return AllocError;
}
data = (char *) rep + SIZEOF (fsListFontsReply);
err = Successful;
/* copy data into FontPathRecord */
for (i = 0; i < rep->nFonts; i++)
{
length = *(unsigned char *)data++;
err = AddFontNamesName(blist->names, data, length);
if (err != Successful)
break;
data += length;
}
_fs_done_read (conn, rep->length << 2);
return err;
}
static int
fs_send_list_fonts(pointer client, FontPathElementPtr fpe, char *pattern,
int patlen, int maxnames, FontNamesPtr newnames)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
FSBlockDataPtr blockrec;
FSBlockedListPtr blockedlist;
fsListFontsReq req;
if (conn->blockState & FS_GIVE_UP)
return BadFontName;
/* make a new block record, and add it to the end of the list */
blockrec = fs_new_block_rec(fpe, client, FS_LIST_FONTS);
if (!blockrec)
return AllocError;
blockedlist = (FSBlockedListPtr) blockrec->data;
blockedlist->names = newnames;
if (conn->blockState & (FS_BROKEN_CONNECTION | FS_RECONNECTING))
{
_fs_pending_reply (conn);
return Suspended;
}
_fs_client_access (conn, client, FALSE);
_fs_client_resolution(conn);
/* send the request */
req.reqType = FS_ListFonts;
req.maxNames = maxnames;
req.nbytes = patlen;
req.length = (SIZEOF(fsListFontsReq) + patlen + 3) >> 2;
_fs_add_req_log(conn, FS_ListFonts);
_fs_write(conn, (char *) &req, SIZEOF(fsListFontsReq));
_fs_write_pad(conn, (char *) pattern, patlen);
blockrec->sequenceNumber = conn->current_seq;
#ifdef NCD
if (configData.ExtendedFontDiags) {
char buf[256];
memcpy(buf, pattern, MIN(256, patlen));
buf[MIN(256, patlen)] = '\0';
printf("Listing fonts on pattern \"%s\" from font server \"%s\"\n",
buf, fpe->name);
}
#endif
_fs_prepare_for_reply (conn);
return Suspended;
}
static int
fs_list_fonts(pointer client, FontPathElementPtr fpe,
char *pattern, int patlen, int maxnames, FontNamesPtr newnames)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
FSBlockDataPtr blockrec;
int err;
/* see if the result is already there */
for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
{
if (blockrec->type == FS_LIST_FONTS && blockrec->client == client)
{
err = blockrec->errcode;
if (err == StillWorking)
return Suspended;
_fs_remove_block_rec(conn, blockrec);
return err;
}
}
/* didn't find waiting record, so send a new one */
return fs_send_list_fonts(client, fpe, pattern, patlen, maxnames, newnames);
}
/*
* Read a single list info reply and restart for the next reply
*/
static int
fs_read_list_info(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
{
FSBlockedListInfoPtr binfo = (FSBlockedListInfoPtr) blockrec->data;
fsListFontsWithXInfoReply *rep;
char *buf;
FSFpePtr conn = (FSFpePtr) fpe->private;
fsPropInfo *pi;
fsPropOffset *po;
pointer pd;
int ret;
int err;
/* clean up anything from the last trip */
_fs_free_props (&binfo->info);
rep = (fsListFontsWithXInfoReply *) fs_get_reply (conn, &ret);
if (!rep || rep->type == FS_Error)
{
if (ret == FSIO_BLOCK)
return StillWorking;
binfo->status = FS_LFWI_FINISHED;
err = AllocError;
goto done;
}
/*
* Normal termination -- the list ends with a name of length 0
*/
if (rep->nameLength == 0)
{
#ifdef DEBUG
fprintf (stderr, "fs_read_list_info done\n");
#endif
binfo->status = FS_LFWI_FINISHED;
err = BadFontName;
goto done;
}
buf = (char *) rep + SIZEOF (fsListFontsWithXInfoReply);
/*
* The original FS implementation didn't match
* the spec, version 1 was respecified to match the FS.
* Version 2 matches the original intent
*/
if (conn->fsMajorVersion <= 1)
{
memcpy (binfo->name, buf, rep->nameLength);
buf += _fs_pad_length (rep->nameLength);
}
pi = (fsPropInfo *) buf;
buf += SIZEOF (fsPropInfo);
po = (fsPropOffset *) buf;
buf += pi->num_offsets * SIZEOF (fsPropOffset);
pd = (pointer) buf;
buf += pi->data_len;
if (conn->fsMajorVersion > 1)
{
memcpy (binfo->name, buf, rep->nameLength);
buf += _fs_pad_length (rep->nameLength);
}
#ifdef DEBUG
binfo->name[rep->nameLength] = '\0';
fprintf (stderr, "fs_read_list_info %s\n", binfo->name);
#endif
err = _fs_convert_lfwi_reply(conn, &binfo->info, rep, pi, po, pd);
if (err != Successful)
{
binfo->status = FS_LFWI_FINISHED;
goto done;
}
binfo->namelen = rep->nameLength;
binfo->remaining = rep->nReplies;
binfo->status = FS_LFWI_REPLY;
/* disable this font server until we've processed this response */
_fs_unmark_block (conn, FS_COMPLETE_REPLY);
FD_CLR(conn->fs_fd, &_fs_fd_mask);
done:
_fs_done_read (conn, rep->length << 2);
return err;
}
/* ARGSUSED */
static int
fs_start_list_with_info(pointer client, FontPathElementPtr fpe,
char *pattern, int len, int maxnames, pointer *pdata)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
FSBlockDataPtr blockrec;
FSBlockedListInfoPtr binfo;
fsListFontsWithXInfoReq req;
if (conn->blockState & FS_GIVE_UP)
return BadFontName;
/* make a new block record, and add it to the end of the list */
blockrec = fs_new_block_rec(fpe, client, FS_LIST_WITH_INFO);
if (!blockrec)
return AllocError;
binfo = (FSBlockedListInfoPtr) blockrec->data;
bzero((char *) binfo, sizeof(FSBlockedListInfoRec));
binfo->status = FS_LFWI_WAITING;
if (conn->blockState & (FS_BROKEN_CONNECTION | FS_RECONNECTING))
{
_fs_pending_reply (conn);
return Suspended;
}
_fs_client_access (conn, client, FALSE);
_fs_client_resolution(conn);
/* send the request */
req.reqType = FS_ListFontsWithXInfo;
req.maxNames = maxnames;
req.nbytes = len;
req.length = (SIZEOF(fsListFontsWithXInfoReq) + len + 3) >> 2;
_fs_add_req_log(conn, FS_ListFontsWithXInfo);
(void) _fs_write(conn, (char *) &req, SIZEOF(fsListFontsWithXInfoReq));
(void) _fs_write_pad(conn, pattern, len);
blockrec->sequenceNumber = conn->current_seq;
#ifdef NCD
if (configData.ExtendedFontDiags) {
char buf[256];
memcpy(buf, pattern, MIN(256, len));
buf[MIN(256, len)] = '\0';
printf("Listing fonts with info on pattern \"%s\" from font server \"%s\"\n",
buf, fpe->name);
}
#endif
_fs_prepare_for_reply (conn);
return Successful;
}
/* ARGSUSED */
static int
fs_next_list_with_info(pointer client, FontPathElementPtr fpe,
char **namep, int *namelenp,
FontInfoPtr *pFontInfo, int *numFonts,
pointer private)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
FSBlockDataPtr blockrec;
FSBlockedListInfoPtr binfo;
int err;
/* see if the result is already there */
for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
if (blockrec->type == FS_LIST_WITH_INFO && blockrec->client == client)
break;
if (!blockrec)
{
/* The only good reason for not finding a blockrec would be if
disconnect/reconnect to the font server wiped it out and the
code that called us didn't do the right thing to create
another one. Under those circumstances, we need to return an
error to prevent that code from attempting to interpret the
information we don't return. */
return BadFontName;
}
binfo = (FSBlockedListInfoPtr) blockrec->data;
if (binfo->status == FS_LFWI_WAITING)
return Suspended;
*namep = binfo->name;
*namelenp = binfo->namelen;
*pFontInfo = &binfo->info;
*numFonts = binfo->remaining;
/* Restart reply processing from this font server */
FD_SET(conn->fs_fd, &_fs_fd_mask);
if (fs_reply_ready (conn))
_fs_mark_block (conn, FS_COMPLETE_REPLY);
err = blockrec->errcode;
switch (binfo->status) {
case FS_LFWI_FINISHED:
_fs_remove_block_rec(conn, blockrec);
break;
case FS_LFWI_REPLY:
binfo->status = FS_LFWI_WAITING;
blockrec->errcode = StillWorking;
conn->blockedReplyTime = GetTimeInMillis () + FontServerRequestTimeout;
_fs_mark_block (conn, FS_PENDING_REPLY);
break;
}
return err;
}
/*
* Called when client exits
*/
static void
fs_client_died(pointer client, FontPathElementPtr fpe)
{
FSFpePtr conn = (FSFpePtr) fpe->private;
FSBlockDataPtr blockrec,
depending;
FSClientPtr *prev, cur;
fsFreeACReq freeac;
for (prev = &conn->clients; (cur = *prev); prev = &cur->next)
{
if (cur->client == client) {
freeac.reqType = FS_FreeAC;
freeac.id = cur->acid;
freeac.length = sizeof (fsFreeACReq) >> 2;
_fs_add_req_log(conn, FS_FreeAC);
_fs_write (conn, (char *) &freeac, sizeof (fsFreeACReq));
*prev = cur->next;
xfree (cur);
break;
}
}
/* find a pending requests */
for (blockrec = conn->blockedRequests; blockrec; blockrec = blockrec->next)
if (blockrec->client == client)
break;
if (!blockrec)
return;
/* replace the client pointers in this block rec with the chained one */
if ((depending = blockrec->depending))
{
blockrec->client = depending->client;
blockrec->depending = depending->depending;
blockrec = depending;
}
fs_abort_blockrec(conn, blockrec);
}
static void
_fs_client_access (FSFpePtr conn, pointer client, Bool sync)
{
FSClientPtr *prev, cur;
fsCreateACReq crac;
fsSetAuthorizationReq setac;
char *authorizations;
int authlen;
Bool new_cur = FALSE;
#ifdef DEBUG
if (conn->blockState & (FS_RECONNECTING|FS_BROKEN_CONNECTION))
{
fprintf (stderr, "Sending requests without a connection\n");
}
#endif
for (prev = &conn->clients; (cur = *prev); prev = &cur->next)
{
if (cur->client == client)
{
if (prev != &conn->clients)
{
*prev = cur->next;
cur->next = conn->clients;
conn->clients = cur;
}
break;
}
}
if (!cur)
{
cur = (FSClientPtr) xalloc (sizeof (FSClientRec));
if (!cur)
return;
cur->client = client;
cur->next = conn->clients;
conn->clients = cur;
cur->acid = GetNewFontClientID ();
new_cur = TRUE;
}
if (new_cur || cur->auth_generation != client_auth_generation(client))
{
if (!new_cur)
{
fsFreeACReq freeac;
freeac.reqType = FS_FreeAC;
freeac.id = cur->acid;
freeac.length = sizeof (fsFreeACReq) >> 2;
_fs_add_req_log(conn, FS_FreeAC);
_fs_write (conn, (char *) &freeac, sizeof (fsFreeACReq));
}
crac.reqType = FS_CreateAC;
crac.num_auths = set_font_authorizations(&authorizations, &authlen,
client);
authlen = crac.num_auths ? (authlen + 3) & ~0x3 : 0;
crac.length = (sizeof (fsCreateACReq) + authlen) >> 2;
crac.acid = cur->acid;
_fs_add_req_log(conn, FS_CreateAC);
_fs_write(conn, (char *) &crac, sizeof (fsCreateACReq));
_fs_write(conn, authorizations, authlen);
/* ignore reply; we don't even care about it */
conn->curacid = 0;
cur->auth_generation = client_auth_generation(client);
}
if (conn->curacid != cur->acid)
{
setac.reqType = FS_SetAuthorization;
setac.length = sizeof (fsSetAuthorizationReq) >> 2;
setac.id = cur->acid;
_fs_add_req_log(conn, FS_SetAuthorization);
_fs_write(conn, (char *) &setac, sizeof (fsSetAuthorizationReq));
conn->curacid = cur->acid;
}
}
/*
* Poll a pending connect
*/
static int
_fs_check_connect (FSFpePtr conn)
{
int ret;
ret = _fs_poll_connect (conn->trans_conn, 0);
switch (ret) {
case FSIO_READY:
conn->fs_fd = _FontTransGetConnectionNumber (conn->trans_conn);
FD_SET (conn->fs_fd, &_fs_fd_mask);
break;
case FSIO_BLOCK:
break;
}
return ret;
}
/*
* Return an FSIO status while waiting for the completed connection
* reply to arrive
*/
static fsConnSetup *
_fs_get_conn_setup (FSFpePtr conn, int *error, int *setup_len)
{
int ret;
char *data;
int headlen;
int len;
fsConnSetup *setup;
fsConnSetupAccept *accept;
ret = _fs_start_read (conn, SIZEOF (fsConnSetup), &data);
if (ret != FSIO_READY)
{
*error = ret;
return 0;
}
setup = (fsConnSetup *) data;
if (setup->major_version > FS_PROTOCOL)
{
*error = FSIO_ERROR;
return 0;
}
headlen = (SIZEOF (fsConnSetup) +
(setup->alternate_len << 2) +
(setup->auth_len << 2));
/* On anything but Success, no extra data is sent */
if (setup->status != AuthSuccess)
{
len = headlen;
}
else
{
ret = _fs_start_read (conn, headlen + SIZEOF (fsConnSetupAccept), &data);
if (ret != FSIO_READY)
{
*error = ret;
return 0;
}
setup = (fsConnSetup *) data;
accept = (fsConnSetupAccept *) (data + headlen);
len = headlen + (accept->length << 2);
}
ret = _fs_start_read (conn, len, &data);
if (ret != FSIO_READY)
{
*error = ret;
return 0;
}
*setup_len = len;
return (fsConnSetup *) data;
}
static int
_fs_send_conn_client_prefix (FSFpePtr conn)
{
fsConnClientPrefix req;
int endian;
int ret;
/* send setup prefix */
endian = 1;
if (*(char *) &endian)
req.byteOrder = 'l';
else
req.byteOrder = 'B';
req.major_version = FS_PROTOCOL;
req.minor_version = FS_PROTOCOL_MINOR;
/* XXX add some auth info here */
req.num_auths = 0;
req.auth_len = 0;
ret = _fs_write (conn, (char *) &req, SIZEOF (fsConnClientPrefix));
if (ret != FSIO_READY)
return FSIO_ERROR;
conn->blockedConnectTime = GetTimeInMillis () + FontServerRequestTimeout;
return ret;
}
static int
_fs_recv_conn_setup (FSFpePtr conn)
{
int ret = FSIO_ERROR;
fsConnSetup *setup;
FSFpeAltPtr alts;
int i, alt_len;
int setup_len;
char *alt_save, *alt_names;
setup = _fs_get_conn_setup (conn, &ret, &setup_len);
if (!setup)
return ret;
conn->current_seq = 0;
conn->fsMajorVersion = setup->major_version;
/*
* Create an alternate list from the initial server, but
* don't chain looking for alternates.
*/
if (conn->alternate == 0)
{
/*
* free any existing alternates list, allowing the list to
* be updated
*/
if (conn->alts)
{
xfree (conn->alts);
conn->alts = 0;
conn->numAlts = 0;
}
if (setup->num_alternates)
{
alts = (FSFpeAltPtr) xalloc (setup->num_alternates *
sizeof (FSFpeAltRec) +
(setup->alternate_len << 2));
if (alts)
{
alt_names = (char *) (setup + 1);
alt_save = (char *) (alts + setup->num_alternates);
for (i = 0; i < setup->num_alternates; i++)
{
alts[i].subset = alt_names[0];
alt_len = alt_names[1];
alts[i].name = alt_save;
memcpy (alt_save, alt_names + 2, alt_len);
alt_save[alt_len] = '\0';
alt_save += alt_len + 1;
alt_names += _fs_pad_length (alt_len + 2);
}
conn->numAlts = setup->num_alternates;
conn->alts = alts;
}
}
}
_fs_done_read (conn, setup_len);
if (setup->status != AuthSuccess)
return FSIO_ERROR;
return FSIO_READY;
}
static int
_fs_open_server (FSFpePtr conn)
{
int ret;
char *servername;
if (conn->alternate == 0)
servername = conn->servername;
else
servername = conn->alts[conn->alternate-1].name;
conn->trans_conn = _fs_connect (servername, &ret);
conn->blockedConnectTime = GetTimeInMillis () + FS_RECONNECT_WAIT;
return ret;
}
static char *
_fs_catalog_name (char *servername)
{
char *sp;
sp = strchr (servername, '/');
if (!sp)
return 0;
return strrchr (sp + 1, '/');
}
static int
_fs_send_init_packets (FSFpePtr conn)
{
fsSetResolutionReq srreq;
fsSetCataloguesReq screq;
int num_cats,
clen;
char *catalogues;
char *cat;
char len;
char *end;
int num_res;
FontResolutionPtr res;
#define CATALOGUE_SEP '+'
res = GetClientResolutions(&num_res);
if (num_res)
{
srreq.reqType = FS_SetResolution;
srreq.num_resolutions = num_res;
srreq.length = (SIZEOF(fsSetResolutionReq) +
(num_res * SIZEOF(fsResolution)) + 3) >> 2;
_fs_add_req_log(conn, FS_SetResolution);
if (_fs_write(conn, (char *) &srreq, SIZEOF(fsSetResolutionReq)) != FSIO_READY)
return FSIO_ERROR;
if (_fs_write_pad(conn, (char *) res, (num_res * SIZEOF(fsResolution))) != FSIO_READY)
return FSIO_ERROR;
}
catalogues = 0;
if (conn->alternate != 0)
catalogues = _fs_catalog_name (conn->alts[conn->alternate-1].name);
if (!catalogues)
catalogues = _fs_catalog_name (conn->servername);
if (!catalogues)
{
conn->has_catalogues = FALSE;
return FSIO_READY;
}
conn->has_catalogues = TRUE;
/* turn cats into counted list */
catalogues++;
cat = catalogues;
num_cats = 0;
clen = 0;
while (*cat)
{
num_cats++;
end = strchr(cat, CATALOGUE_SEP);
if (!end)
end = cat + strlen (cat);
clen += (end - cat) + 1; /* length byte + string */
cat = end;
}
screq.reqType = FS_SetCatalogues;
screq.num_catalogues = num_cats;
screq.length = (SIZEOF(fsSetCataloguesReq) + clen + 3) >> 2;
_fs_add_req_log(conn, FS_SetCatalogues);
if (_fs_write(conn, (char *) &screq, SIZEOF(fsSetCataloguesReq)) != FSIO_READY)
return FSIO_ERROR;
while (*cat)
{
num_cats++;
end = strchr(cat, CATALOGUE_SEP);
if (!end)
end = cat + strlen (cat);
len = end - cat;
if (_fs_write (conn, &len, 1) != FSIO_READY)
return FSIO_ERROR;
if (_fs_write (conn, cat, (int) len) != FSIO_READY)
return FSIO_ERROR;
cat = end;
}
if (_fs_write (conn, "....", _fs_pad_length (clen) - clen) != FSIO_READY)
return FSIO_ERROR;
return FSIO_READY;
}
static int
_fs_send_cat_sync (FSFpePtr conn)
{
fsListCataloguesReq lcreq;
/*
* now sync up with the font server, to see if an error was generated
* by a bogus catalogue
*/
lcreq.reqType = FS_ListCatalogues;
lcreq.length = (SIZEOF(fsListCataloguesReq)) >> 2;
lcreq.maxNames = 0;
lcreq.nbytes = 0;
_fs_add_req_log(conn, FS_SetCatalogues);
if (_fs_write(conn, (char *) &lcreq, SIZEOF(fsListCataloguesReq)) != FSIO_READY)
return FSIO_ERROR;
conn->blockedConnectTime = GetTimeInMillis () + FontServerRequestTimeout;
return FSIO_READY;
}
static int
_fs_recv_cat_sync (FSFpePtr conn)
{
fsGenericReply *reply;
fsError *error;
int err;
int ret;
reply = fs_get_reply (conn, &err);
if (!reply)
return err;
ret = FSIO_READY;
if (reply->type == FS_Error)
{
error = (fsError *) reply;
if (error->major_opcode == FS_SetCatalogues)
ret = FSIO_ERROR;
}
_fs_done_read (conn, reply->length << 2);
return ret;
}
static void
_fs_close_server (FSFpePtr conn)
{
_fs_unmark_block (conn, FS_PENDING_WRITE|FS_BROKEN_WRITE|FS_COMPLETE_REPLY|FS_BROKEN_CONNECTION);
if (conn->trans_conn)
{
_FontTransClose (conn->trans_conn);
conn->trans_conn = 0;
_fs_io_reinit (conn);
}
if (conn->fs_fd >= 0)
{
FD_CLR (conn->fs_fd, &_fs_fd_mask);
conn->fs_fd = -1;
}
conn->fs_conn_state = FS_CONN_UNCONNECTED;
}
static int
_fs_do_setup_connection (FSFpePtr conn)
{
int ret;
do
{
#ifdef DEBUG
fprintf (stderr, "fs_do_setup_connection state %d\n", conn->fs_conn_state);
#endif
switch (conn->fs_conn_state) {
case FS_CONN_UNCONNECTED:
ret = _fs_open_server (conn);
if (ret == FSIO_BLOCK)
conn->fs_conn_state = FS_CONN_CONNECTING;
break;
case FS_CONN_CONNECTING:
ret = _fs_check_connect (conn);
break;
case FS_CONN_CONNECTED:
ret = _fs_send_conn_client_prefix (conn);
break;
case FS_CONN_SENT_PREFIX:
ret = _fs_recv_conn_setup (conn);
break;
case FS_CONN_RECV_INIT:
ret = _fs_send_init_packets (conn);
if (conn->has_catalogues)
ret = _fs_send_cat_sync (conn);
break;
case FS_CONN_SENT_CAT:
if (conn->has_catalogues)
ret = _fs_recv_cat_sync (conn);
else
ret = FSIO_READY;
break;
default:
ret = FSIO_READY;
break;
}
switch (ret) {
case FSIO_READY:
if (conn->fs_conn_state < FS_CONN_RUNNING)
conn->fs_conn_state++;
break;
case FSIO_BLOCK:
if (TimeCmp (GetTimeInMillis (), <, conn->blockedConnectTime))
break;
ret = FSIO_ERROR;
/* fall through... */
case FSIO_ERROR:
_fs_close_server (conn);
/*
* Try the next alternate
*/
if (conn->alternate < conn->numAlts)
{
conn->alternate++;
ret = FSIO_READY;
}
else
conn->alternate = 0;
break;
}
} while (conn->fs_conn_state != FS_CONN_RUNNING && ret == FSIO_READY);
if (ret == FSIO_READY)
conn->generation = ++generationCount;
return ret;
}
static int
_fs_wait_connect (FSFpePtr conn)
{
int ret;
for (;;)
{
ret = _fs_do_setup_connection (conn);
if (ret != FSIO_BLOCK)
break;
if (conn->fs_conn_state <= FS_CONN_CONNECTING)
ret = _fs_poll_connect (conn->trans_conn, 1000);
else
ret = _fs_wait_for_readable (conn, 1000);
if (ret == FSIO_ERROR)
break;
}
return ret;
}
/*
* Poll a connection in the process of reconnecting
*/
static void
_fs_check_reconnect (FSFpePtr conn)
{
int ret;
ret = _fs_do_setup_connection (conn);
switch (ret) {
case FSIO_READY:
_fs_unmark_block (conn, FS_RECONNECTING|FS_GIVE_UP);
_fs_restart_connection (conn);
break;
case FSIO_BLOCK:
break;
case FSIO_ERROR:
conn->brokenConnectionTime = GetTimeInMillis () + FS_RECONNECT_POLL;
break;
}
}
/*
* Start the reconnection process
*/
static void
_fs_start_reconnect (FSFpePtr conn)
{
if (conn->blockState & FS_RECONNECTING)
return;
conn->alternate = 0;
_fs_mark_block (conn, FS_RECONNECTING);
_fs_unmark_block (conn, FS_BROKEN_CONNECTION);
_fs_check_reconnect (conn);
}
static FSFpePtr
_fs_init_conn (char *servername)
{
FSFpePtr conn;
conn = xalloc (sizeof (FSFpeRec) + strlen (servername) + 1);
if (!conn)
return 0;
memset (conn, '\0', sizeof (FSFpeRec));
if (!_fs_io_init (conn))
{
xfree (conn);
return 0;
}
conn->servername = (char *) (conn + 1);
conn->fs_conn_state = FS_CONN_UNCONNECTED;
conn->fs_fd = -1;
strcpy (conn->servername, servername);
return conn;
}
static void
_fs_free_conn (FSFpePtr conn)
{
_fs_close_server (conn);
_fs_io_fini (conn);
if (conn->alts)
xfree (conn->alts);
xfree (conn);
}
/*
* called at server init time
*/
void
fs_register_fpe_functions(void)
{
RegisterFPEFunctions(fs_name_check,
fs_init_fpe,
fs_free_fpe,
fs_reset_fpe,
fs_open_font,
fs_close_font,
fs_list_fonts,
fs_start_list_with_info,
fs_next_list_with_info,
fs_wakeup,
fs_client_died,
_fs_load_glyphs,
NULL,
NULL,
NULL);
}
static int
check_fs_open_font(pointer client, FontPathElementPtr fpe, Mask flags,
char *name, int namelen,
fsBitmapFormat format, fsBitmapFormatMask fmask,
XID id, FontPtr *ppfont,
char **alias, FontPtr non_cachable_font)
{
if (XpClientIsBitmapClient(client))
return (fs_open_font(client, fpe, flags, name, namelen, format,
fmask, id, ppfont, alias, non_cachable_font) );
return BadFontName;
}
static int
check_fs_list_fonts(pointer client, FontPathElementPtr fpe,
char *pattern, int patlen, int maxnames,
FontNamesPtr newnames)
{
if (XpClientIsBitmapClient(client))
return (fs_list_fonts(client, fpe, pattern, patlen, maxnames,
newnames));
return BadFontName;
}
static int
check_fs_start_list_with_info(pointer client, FontPathElementPtr fpe,
char *pattern, int len, int maxnames,
pointer *pdata)
{
if (XpClientIsBitmapClient(client))
return (fs_start_list_with_info(client, fpe, pattern, len, maxnames,
pdata));
return BadFontName;
}
static int
check_fs_next_list_with_info(pointer client, FontPathElementPtr fpe,
char **namep, int *namelenp,
FontInfoPtr *pFontInfo, int *numFonts,
pointer private)
{
if (XpClientIsBitmapClient(client))
return (fs_next_list_with_info(client, fpe, namep, namelenp, pFontInfo,
numFonts,private));
return BadFontName;
}
void
check_fs_register_fpe_functions(void)
{
RegisterFPEFunctions(fs_name_check,
fs_init_fpe,
fs_free_fpe,
fs_reset_fpe,
check_fs_open_font,
fs_close_font,
check_fs_list_fonts,
check_fs_start_list_with_info,
check_fs_next_list_with_info,
fs_wakeup,
fs_client_died,
_fs_load_glyphs,
NULL,
NULL,
NULL);
}
|